How to add nil value to Swift Dictionary? - swift

I have made a request to my server in my app. And posted data something like this.Server side is waiting for all parameters even they are nil. But i couldn't add nil values to dictionary.
var postDict = Dictionary<String,AnyObject>
postDict[pass]=123
postDict[name]="ali"
postDict[surname]=nil // dictionary still has only pass and name variables.
Is there a way to add nil value to 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??.

You can use the updateValue method:
postDict.updateValue(nil, forKey: surname)

As documented in here, setting nil for a key in dictionary means removing the element itself.
If you want null when converting to JSON for example, you can use NSNull()
var postDict = Dictionary<String,AnyObject>()
postDict["pass"]=123
postDict["name"]="ali"
postDict["surname"]=NSNull()
let jsonData = NSJSONSerialization.dataWithJSONObject(postDict, options: NSJSONWritingOptions.allZeros, error: nil)!
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)!
// -> {"pass":123,"surname":null,"name":"ali"}

postDict[surname] = Optional<AnyObject>(nil)

You can use the Optional type
var postDict = ["pass": 123, "name": "ali", "surname": Optional()]

Below dictionary will hold one key with nil value
var dict = [String:Any?]()
dict["someKey"] = nil as Any?

To add a nil value to a dictionary in Swift, your dictionary's values must be of the Optional type.
Consider a Person class:
class Person {
let name: String
weak var spouse: Person?
init(name: String, spouse: Person?) {
self.name = name
self.spouse = spouse
}
}
Instances of the Person type can have a name and an optional spouse. Create two instances, and add the first to a dictionary:
let p1 = Person(name: "John", spouse: nil)
let p2 = Person(name: "Doe", spouse: p1)
p1.spouse = p2
var people = [p1.name: p1.spouse]
This dictionary (called people) maps names to spouses, and is of type [String: Person?]. You now have a dictionary with a value of Optional type: Person?.
To update the value of the key p1.name to be nil, use the updateValue(_: forKey:) method on the Dictionary type.
people.updateValue(nil, forKey: p1.name)
people[p1.name]
The value for the key p1.name is now nil. Using updateValue(_: forKey:) is a bit more straightforward in this case because it doesn't involve making a throwaway instance, setting it to nil, and assigning that instance to a key in a dictionary.
NB: See rintaro's answer for inserting null into a post's dictionary.

var dict = [Int:Int?]()
dict[0] = (Int?).none // <--- sets to value nil
dict[0] = nil // <-- removes
dict[0] = .none // <-- same as previous, but more expressive
switch dict[0] {
case .none:
Swift.print("Value does not exist")
case .some(let value):
if let value = value {
Swift.print("Value exists and is", value)
} else {
Swift.print("Value exists and is nil")
}
}

postDict[surname]=nil
When you use subscript to set nil. It deletes the key if exists. In this case key surname will be removed from dictionary if exists.
To set value as nil, there are certain ways.
postDict.updateValue(nil, forKey: surname)
or
let anyObjectNil : AnyObject? = nil
postDict[surname] = anyObjectNil
or
postDict[surname] = nil as AnyObject?

Related

How setting 'nil' as a value of [String: String] dictionary is valid?

I'm curious why this snippet works correctly in Playground:
var dict = [String: String]()
dict["key"] = nil
let value = dict["key"]
we can see that I declared Dictionary with non optional values, after checking it in Playground it works similarly to dictionary declared as [String: String?]
My question is where is the difference in terms of setting nil value between dictionary declared as [String: String] and [String: String?] ?
dict["key"] = nil is a shorthand to removing the key from the dictionary (same as using dict.removeValue(forKey: "key")). If there was a value under the "key" key, after this line the whole entry is removed from the dictionary (both the key and the value).
Read the subscripts docs to learn more:
If you assign nil as the value for the given key, the dictionary removes that key and its associated value.
In the following example, the key-value pair for the key "Aquamarine" is removed from the dictionary by assigning nil to the key-based subscript.
hues["Aquamarine"] = nil
print(hues)
// Prints "["Coral": 18, "Heliotrope": 296, "Cerise": 330]"
let value = dict["key"] gets the value for the key, and by definition returns nil if there is no entry for the given key (which is in your case).
According to docs, subscript returns either the value, or nil, if the key is not in the dictionary:
The value associated with key if key is in the dictionary; otherwise, nil.
dict["key"] = nil means you are removing the item from dictionary. The return type of dict[someKey] is Optional as the key may not be there.
You could consider implementing:
dict["key"] = nil
as the same as calling removeValue(forKey:):
dict.removeValue(forKey: "key")
By default, getting a value from a dictionary would returns an optional value:
var dict = [String: String]()
dict["key"] = "Hello"
let value = dict["key"] // Optional("Hello")
Note that the type of value is optional string (String?).

What's the correct way to create a dictionary typealias?

If I want to define a typealias for a Dictionary, which accept String as the key, and String for the value, but I also want it to can accept nil, which one of these definition is correct?
typealias dictString = Dictionary<String, String?>
or
typealias dictString = Dictionary<String, String>
or
typealias dictString = [String:String?]
or
typealias dictString = [String:String]
Because I use the first one, and now each time I want to unwrap a value, I need to unwrap it twice (e.g. dict["name"]!!) so I began to wondering whether I really need to add the question mark or not (but I need the dictionary to still able to take nil value). Thanks.
The second and fourth are okay.
You don't need to make the value optional in most cases. You can set a key's value to nil even if you did not declare the value as an optional type:
var dict = [String: String]()
dict["Hello"] = nil
And when you access "Hello", it returns nil. Fantastic!
In fact, setting a key's value to nil means to "delete the key". This can cause unexpected results if you don't know this.
For example,
var dict = [String: String]()
dict["Hello"] = nil
print(dict.keys.contains("Hello"))
prints false.
If you need to check if a key exists even if its value is nil, then you have to make the value an optional type.
A dictionary will return nil for any key that's not present. Now, if you want to be able to actually have the key present but have the value nil then version 1 and 3 are the way to go and you keep the double unwrapping. Otherwise use 2 or 4.
Update
Example:
Version 1 & 3:
typealias DictString = Dictionary<String, String?> // or [String: String?]
var myDict: DictString = ["MyKey1": "MyVal1", "MyKey2": nil]
var value1: String? = myDict["MyKey1"]! // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"]! // This will return nil
Version 2 & 4:
typealias DictString = Dictionary<String, String> // or [String: String]
var myDict: DictString = ["MyKey1": "MyVal1"]
var value1: String? = myDict["MyKey1"] // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"] // This will return nil
Now the difference between the two is that the first case actually stores the keys. So if you ever do this:
var allKeys: [String] = Array(myDict.keys)
You'll get two different results:
1 & 3: allKeys will be ["MyKey1", "MyKey2"]
2 & 4: allKeys will be ["MyKey1"]
P.S: A good practice is to use upper cases for types' names. So I would suggest to changes your typealias to DictString.

Create empty dictionary

I have a dictionary initialized
var dictionary = [String: [Double]]()
And I want to append
dictionary["Hello"].append(0.0)
but this gives me error "nil".
I tried to solve this by
extension Dictionary {
func appendish(key: String, value: Double) {
if self[key] == nil {
this give me error "Ambiguous reference to member 'subscript'"
}
}
}
How do I solve this? Been stuck at this for hours.
Subscripting a Dictionary with a key returns an optional of type Value?. In your case, dictionary["Hello"] returns a [Double]?. This optionality models the possibility that the dictionary doesn't contain a value for the given key.
If you're only dealing with static data, it's best to just use a literal expression:
let dictionary = [
"Hello": [0.0]
]
If you're using dynamic data, then there are several ways to do what you're trying to achieve, depending on how you would like to handle the nil case:
Use optional chaining
dictionary["Hello"]?.append(0.0)
This appends to the array stored for the key "Hello", but does nothing if there's no such value for that key.
This has the downside of making bugs harder to catch, because the consequence of the silent nil case might not be observed until long after this part of the code has run.
Use force unwrapping
dictionary["Hello"]!.append(0.0)
This appends to the array stored for the key "Hello", but crashes the program if there's no such value for that key.
Unlike optional chaining, this makes it easy to catch the point of failure at runtime. Of course, it comes with the drawback of crashing your program.
Handle the nil case in your own way
if var array = dictionary["Hello"] {
dictionary["Hello"] = nil // This line is a performance optimisation that removes the need for array to be copied
array.append(0.0)
dictionary["Hello"] = array
}
else {
print("No array for the key \"Hello\"") // Handle this as you wish
}
A dictionary look up returns an Optional value because the key might not exist, in which case it returns nil.
If your intention is to append to the array if it exists or create one if there isn't one yet, then the nil coalescing operator ?? comes in handy:
var dict = [String: [Double]]()
dict["hello"] = (dict["hello"] ?? []) + [1]
print(dict) // ["hello": [1.0]]
dict["hello"] = (dict["hello"] ?? []) + [2]
print(dict) // ["hello": [1.0, 2.0]]
This method does create a new array instead of mutating the existing one.
There are a few ways you can do this. Firstly, this is incorrect code:
dictionary["Hello"].append(0.0)
There might not be an array associated with the key "Hello", in which case nil will be returned by the subscript of the dictionary. So you need to unwrap it, either forced or un-forced:
dictionary["Hello"]?.append(0.0)
// or
dictionary["Hello"]!.append(0.0)
But I think what you really want to do is
if dictionary["Hello"] != nil {
dictionary["Hello"]!.append(0.0)
} else {
dictionary["Hello"] = [0.0]
}
After a long time of fiddling around with extensions and stuff (I am not familiar with this area of swift), I finally wrote the method appendish method that you were intended to write:
extension Dictionary where Value : RangeReplaceableCollection & ExpressibleByArrayLiteral, Value.Iterator.Element == Value.Element {
mutating func appendish(key: Key, value: Value.Element) {
if self[key] != nil {
self[key]!.append(value)
} else {
self[key] = [value]
}
}
}
// test
var dict = [String: [Double]]()
dict.appendish(key: "Hello", value: 0.0)

Why aren't [String:AnyObject?] and [String:AnyObject] the same type to the swift compiler?

Semantically speaking, [String:AnyObject?] and [String:AnyObject] are the same thing in terms of the way they act, meaning they will return the same thing if I access a key that wasn't set and setting a key to nil will remove that key from the dictionary. Why aren't they considered the same type?
EDIT: I understand the difference from the compiler point of view from the answers. I decided to put the following code in the playground:
var optional = [String:AnyObject?]()
var regular = [String:AnyObject]()
//Some control keys
optional["controlkey"] = "valueoptional"
regular["controlkey"] = "valueRegular"
//Set the keys
optional["keyOptional"] = "valueoptional"
regular["keyRegular"] = "valueRegular"
//Unset the keys
optional["keyOptional"] = nil
regular["keyRegular"] = nil
for (key,val) in optional {
print("key: \(key)\tval: \(val)")
}
for (key,val) in regular {
print("key: \(key)\tval: \(val)")
}
To my surprise the optional did not print the key that was set to nil.
Output was as follow:
key: controlkey val: Optional(valueoptional) //The keys for the optional dictionary
key: controlkey val: valueRegular //The keys for the `regular` dictionary
Why doesn't the key that I set to nil show up?
From the Apple docs:
The Swift language defines the postfix ? as syntactic sugar for the
named type Optional, which is defined in the Swift standard
library.
The type AnyObject? is an enumeration with two cases, None and Some(Wrapped), which are used to represent values that may or may not be present. But AnyObject is responding to one that will be presented.
Setting nil for a key in dictionary means removing the element itself.
Try to set NSNull()
optional["keyOptional"] = NSNull()
Because a nullable type and a non nullable type aren't the same thing from the compiler point of view. It just happens that the Dictionnary interface eventually "blend them" in similar entity, but this is linked with the Dictionnary implementation, not really with the type system.
EDIT: Your update changes the context of the question, but setting to nil is a way to unset from my understanding.
They are different because you can actually store nil into [String:AnyObject?]. You cannot do it using optional[key] = value (subscript operator) because that one has a special behavior for nil (removes value) but you can do it:
var optional: [String:AnyObject?] = ["test": nil]
optional.updateValue(nil, forKey: "test2")
print(optional) // ["test2": nil, "test": nil]
Of course, getting a value from such a dictionary:
print(optional["test"]) // Optional(nil)
results in a double optional Optional<Optional<AnyObject>> (or AnyObject??) and interaction with such types is cumbersome so you should avoid storing nil into dictionaries.
Because setting a key to nil remove that key from the dictionary! From the documentation
Reading a key that is not present in self yields nil. Writing nil as the value for a given key erases that key from self
You can't store nil as a dictionary value. If you need something denote nothingness, use NSNull:
optional["keyOptional"] = NSNull()
regular["keyRegular"] = NSNull()
They aren't the same type because they aren't the same type ;-) Compilers are such literal creatures.
#Sulthan's answer is correct and more complete - I didn't see it until after I hit post on my own answer. But if you look up a key in a [String: AnyObject?] dictionary and a value is present, you'll get an optional wrapped in an optional - the result is an AnyObject??. You'd have to unwrap it twice to use it:
var optional = [String: AnyObject?]()
// I changed it to NSString because String isn't an AnyObject
v
optional["controlkey"] = NSString(string: "valueoptional")
print(optional["controlkey"]) // prints "Optional(Optional(valueoptional))"
if let val = optional["controlkey"] {
print(val) // prints "Optional(valueoptional)"
if let unwrappedVal = val {
print(unwrappedVal) // prints "valueoptional"
}
}

Why do I still need to unwrap Swift dictionary value?

class X {
static let global: [String:String] = [
"x":"x data",
"y":"y data",
"z":"z data"
]
func test(){
let type = "x"
var data:String = X.global[type]!
}
}
I'm getting the error: Value of optional type 'String?' not unwrapped.
Why do I need to use ! after X.global[type]? I'm not using any optional in my dictionary?
Edited:
Even if X.global[type] may not exist for the type, force unwrapping will still crash on runtime. A better approach may be:
if let valExist = X.global[type] {
}
but Xcode is giving me the wrong idea by hinting about optional type.
Dictionary accessor returns optional of its value type because it does not "know" run-time whether certain key is there in the dictionary or not. If it's present, then the associated value is returned, but if it's not then you get nil.
From the documentation:
You can also use subscript syntax to retrieve a value from the dictionary for a particular key. Because it is possible to request a key for which no value exists, a dictionary’s subscript returns an optional value of the dictionary’s value type. If the dictionary contains a value for the requested key, the subscript returns an optional value containing the existing value for that key. Otherwise, the subscript returns nil...
In order to handle the situation properly you need to unwrap the returned optional.
There are several ways:
Option 1:
func test(){
let type = "x"
if var data = X.global[type] {
// Do something with data
}
}
Option 2:
func test(){
let type = "x"
guard var data = X.global[type] else {
// Handle missing value for "type", then either "return" or "break"
}
// Do something with data
}
Option 3:
func test(){
let type = "x"
var data = X.global[type] ?? "Default value for missing keys"
}
If we look at the Dictionary implementation, subscript is returning a ValueType as optional because it doesn't know if the key is exists or not:
//Reading a key that is not present in `self` yields `nil`.
//Writing `nil` as the value for a given key erases that key from `self`.
subscript (key: KeyType) -> ValueType?
So when we try to get a value from our Dictionary we get it as an optional from the subscript; that is we have to unwrap the optional to get the underlying object. As mentioned in earlier answers, option2 is preferred.
guard var data = X.global[type] else {
//key = 'type' doesn't exists
}
//key exists so do something with 'data'