Consider the following example involving the nil coalescing operator ??:
let mysteryInc = ["Fred": "Jones", "Scooby-Doo": nil]
let lastname = mysteryInc["Scooby-Doo"] ?? "no last name"
print(lastname == nil) // true
As shown by the last print statement, the result of the nil coalescing operator is nil.
If the nil coalescing operator is supposed to unwrap an Optional, why is it returning nil?
To see what is going on here, assign the dictionary look up to a constant:
let name = mysteryInc["Scooby-Doo"]
print(type(of: name))
Output:
Optional<Optional<String>>
So, name is a double Optional, a String??.
When the nil coalescing operator is applied, it unwraps the outer Optional and leaves an Optional<String> (aka String?). In the example in the question, Swift treats the String literal "no last name" as type String? so that ?? can be applied.
If you examine the value of name you will find that it is Optional(nil). All dictionary look ups return Optionals because the key might not exist (in which case they return nil). In this case, the mysteryInc dictionary is of type [String: String?]. The value corresponding to "Scooby-Doo" is nil which then gets wrapped into an Optional (because of the dictionary look up) to become Optional(nil).
Finally, the value Optional(nil) is unwrapped by ??. Since Optional(nil) != nil, Swift unwraps Optional(nil) and returns nil instead of returning the expected "no last name".
And that is how you can get nil from the nil coalescing operator. It did unwrap the Optional; there just happened to be a nil inside of that Optional.
As @LeoDabus noted in the comments, the correct way to deal with this situation is to conditionally cast the name to String before applying the nil coalescing operator:
let lastname = mysteryInc["Scooby-Doo"] as? String ?? "no last name"
In reality, it is best to avoid having Optionals as values for your dictionary for the very reason that @Sulthan raises:
What does mysteryInc["Fred"] = nil do?
Assigning nil to a dictionary entry removes that entry from the dictionary, so mysteryInc["Fred"] = nil doesn't replace Optional("Jones") with nil, it removes the "Fred" entry altogether. To leave "Fred" in the dictionary and make his last name nil, you need to assign mysteryInc["Fred"] = Optional(nil) or mysteryInc.updateValue(nil, forKey: "Fred").
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With