How to add nil value to Swift Dictionary?
How to add
nil
value to Swift Dictionary?
Basically the same way you add any other value to a dictionary. You first need a dictionary which has a value type that can hold your value. The type AnyObject
cannot have a value nil
. So a dictionary of type [String : AnyObject]
cannot have a value nil
.
If you had a dictionary with a value type that was an optional type, like [String : AnyObject?]
, then it can hold nil
values. For example,
let x : [String : AnyObject?] = ["foo" : nil]
If you want to use the subscript syntax to assign an element, it is a little tricky. Note that a subscript of type [K:V]
has type V?
. The optional is for, when you get it out, indicating whether there is an entry for that key or not, and if so, the value; and when you put it in, it allows you to either set a value or remove the entry (by assigning nil
).
That means for our dictionary of type [String : AnyObject?]
, the subscript has type AnyObject??
. Again, when you put a value into the subscript, the "outer" optional allows you to set a value or remove the entry. If we simply wrote
x["foo"] = nil
the compiler infers that to be nil
of type AnyObject??
, the outer optional, which would mean remove the entry for key "foo"
.
In order to set the value for key "foo"
to the AnyObject?
value nil
, we need to pass in a non-nil
outer optional, containing an inner optional (of type AnyObject?
) of value nil
. In order to do this, we can do
let v : AnyObject? = nil
x["foo"] = v
or
x["foo"] = nil as AnyObject?
Anything that indicates that we have a nil
of AnyObject?
, and not AnyObject??
.
Swift - setting dictionary value to nil confusion
To remove a key in a dictionary of type [A: B]
, you need to set its value to an element of type B?
of value nil
For instance:
var d = ["foo": 1] as [String: Int]
let v: Int? = nil
d["foo"] = v // d is now [:]
or simply
d["foo"] = nil // here nil is casted to Int?
So if we now have
var d = ["foo": nil] as [String: Any?]
A
= String
and B
=Any?
to remove the key/value associated to foo
, we need to set the value to a type B?
= Any??
of value nil
:
let v: Any?? = nil
d["foo"] = v // d is now [:]
What happens when we do
d["foo"] = nil
is that here nil
is casted to Any??
and not Any?
, so its actually different from doing
let v: Any? = nil
d["foo"] = v // d is still ["foo": nil]
and that's why the results are different.
Thanks @sliwinski for opening the discussion with apple that linked us to https://developer.apple.com/swift/blog/?id=12
How do I set a Swift dictionary nil value such that my server receives null for the key?
You should use NSNull
instead of nil
.
Example:
parameters["foo"] = NSNull()
Dictionary value returning nil in Swift
You should use dayDictionary[word.description.lowercased()]
instead of dayDictionary[String(word)]
I tried with Playground and I added some print
lines to for loop ->
for word in components {
print("word is: \(word)")
print("components is: \(components)")
print("previous is: \(previous)")
print("dayDictionary is: \(dayDictionary)")
print("item in dayDict is: \(dayDictionary[word.description.lowercased()])")
if previous == "to" {
print("Hi end")
endIndex = dayDictionary[String(word)] ?? -1
break
}
if word == "to" {
print("Hi Start")
startIndex = dayDictionary[previous] ?? -1
continue
}
if let validIndex = dayDictionary[word.description.lowercased()] {
duration += [validIndex]
print("Valid")
}
previous = String(word)
print("---------------------------------------")
}
Then the output is:
1st iteration
word is: Set
components is: ["Set", "alarm", "Monday", "to", "Friday"]
previous is:
dayDictionary is: ["tuesday": 1, "wednesday": 2, "saturday": 5, "monday": 0, "thursday": 3, "sunday": 6, "friday": 4]
item in dayDict is: nil
---------------------------------------
2nd iteration
word is: alarm
components is: ["Set", "alarm", "Monday", "to", "Friday"]
previous is: Set
dayDictionary is: ["tuesday": 1, "wednesday": 2, "saturday": 5, "monday": 0, "thursday": 3, "sunday": 6, "friday": 4]
item in dayDict is: nil
---------------------------------------
3rd iteration
word is: Monday
components is: ["Set", "alarm", "Monday", "to", "Friday"]
previous is: alarm
dayDictionary is: ["tuesday": 1, "wednesday": 2, "saturday": 5, "monday": 0, "thursday": 3, "sunday": 6, "friday": 4]
item in dayDict is: Optional(0)
Valid
---------------------------------------
4th iteration
word is: to
components is: ["Set", "alarm", "Monday", "to", "Friday"]
previous is: Monday
dayDictionary is: ["tuesday": 1, "wednesday": 2, "saturday": 5, "monday": 0, "thursday": 3, "sunday": 6, "friday": 4]
item in dayDict is: nil
Hi Start
5th iteration
word is: Friday
components is: ["Set", "alarm", "Monday", "to", "Friday"]
previous is: Monday
dayDictionary is: ["tuesday": 1, "wednesday": 2, "saturday": 5, "monday": 0, "thursday": 3, "sunday": 6, "friday": 4]
item in dayDict is: Optional(4)
Valid
---------------------------------------
Please look at the third and fifth iterations. We could get the item from dayDictionary
.
Swift iOS -Get dictionary to accept nil value
Use if let
to ignore nil
values while adding/updating the values in the dictionary.
if let clrThree = colorClass.colorThree {
myDict.updateValue(clrThree, forKey: "thirdKey")
}
if let clrFour = colorClass.colorFour {
myDict.updateValue(clrFour, forKey: "fourthKey")
}
Reason of Crashes is,
you are using !
after the colorClass.colorFour
i.e. you are using as colorClass.colorFour!
.
When swift finds the value of the variable as nil and try to
forcefully unwrap it the application will crash unexpectedly found
nil while unwrapping
Check for nil in dictionary
The way of unwrapping objects of type Any
that contain optionals is kind of weird but you can check that the values aren't nil in your mirror like this:
for x in Mirror(reflecting: cl).children {
if case Optional<Any>.some(let val) = x.value {
print(type(of: val))
colorDict[x.label!] = val
}
}
Elegantly populate dictionary from a struct checking nil values
It's usually not a good idea to have a dictionary with a value that is optional. Dictionaries use the assignment of nil
as an indication that you want to delete a key/value pair from the dictionary. Also, dictionary lookups return an optional value, so if your value is optional you will end up with a double optional that needs to be unwrapped twice.
You can use the fact that assigning nil
deletes a dictionary entry to build up a [String : String]
dictionary by just assigning the values. The ones that are nil
will not go into the dictionary so you won't have to remove them:
struct A {
var first: String?
var second: String?
var third: String?
}
let a = A(first: "one", second: nil, third: "three")
let pairs: [(String, String?)] = [
("first", a.first),
("second", a.second),
("third", a.third)
]
var dictionary = [String : String]()
for (key, value) in pairs {
dictionary[key] = value
}
print(dictionary)
["third": "three", "first": "one"]
As @Hamish noted in the comments, you can use a DictionaryLiteral
(which internally is just an array of tuples) for pairs
which allows you to use the cleaner dictionary syntax:
let pairs: DictionaryLiteral<String,String?> = [
"first": a.first,
"second": a.second,
"third": a.third
]
All of the other code remains the same.
Note: You can just write DictionaryLiteral
and let the compiler infer the types, but I have seen Swift fail to compile or compile very slowly for large dictionary literals. That is why I have shown the use of explicit types here.
Alternatively, you can skip the Array
or DictionaryLiteral
of pairs
and just assign the values directly:
struct A {
var first: String?
var second: String?
var third: String?
}
let a = A(first: "one", second: nil, third: "three")
var dictionary = [String : String]()
dictionary["first"] = a.first
dictionary["second"] = a.second
dictionary["third"] = a.third
print(dictionary)
["third": "three", "first": "one"]
Related Topics
Swift 3 Errors with Additional Data
What Are 3 Items in a Swift Method Parameter For
How to Make That an Object of the Same Class Become the Return Value of Initializer
Decoding Dynamic JSON Structure in Swift 4
Label Showing Top of Screen Instead of Being on the Inputaccessoryview
Interrupted Purchase Not Calling Delegate After Accepting T&C
How to Make a Uiview Grow in Uiview.Animate() with an Accurate Corner Radius
How to Decode Utf8-Literals Like "\Xc3\Xa6" in Swift 5
Get Images from Document Directory Not File Path Swift 3
Different Path Url for Filemanager Everytime I Open the App
Updated Approach to Reauthenticate a User
Update All Value in One Attribute Core Data
Cannot Use Unarchivefromfile to Set Gamescene in Spritekit
Accessing and Manipulating Array Item in an Environmentobject