3

Can someone explain why this works in Swift 3?

var dict: [AnyHashable: Any]
let b: AnyObject? = nil
let c = b as Any

dict = ["a": "aa", "b": c]

If I test

dict["b"] == nil

It returns false. Is it supposed to be right?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Rafael Sacchi
  • 33
  • 1
  • 1
  • 3
  • Using `nil` as a dictionary value in Swift is nonsense because by definition setting a key to `nil` removes the key. – vadian Dec 12 '16 at 21:53
  • I know. The point is: someway I get a dictionary with a `nil` value and cannot remove it, because the check `dict[key] == nil` always returns false – Rafael Sacchi Dec 12 '16 at 22:01
  • Why don't you us it as an optional and check `if let _ = dict["b"]`. This is always a preferred check in swift – jatin kumar malana Dec 12 '16 at 22:04
  • Interesting... Too many compiler magic here. `c` is `Any?`, which is not `nil` because `c` has a value. But the value is `nil`. – Bryan Chen Dec 12 '16 at 22:30
  • Setting it to nil via `dict[key] = nil` indeed removes the value. However a dict can hold an "Optional" as its type. It leads to confusing behavior with the subscript syntax, but you could still do something like `dict.updateValue(nil, forKey: "a")` to set a nil value. A dict with optionals is not really recommended at all though for obvious reasons. – bobDevil Dec 12 '16 at 22:31

2 Answers2

2

You're running into nested optionals. If a dictionary holds a type E, then the dictionary access method returns a value of type E?, either the value if it exists, or nil.

In your case, you've created a dictionary where the value is an optional. So the E above is something like Any?. This means the return value of the getter is E? i.e., Any??

In your case, dict["b"] returns a non-nil optional, containing the value 'nil'

Putting your code in a playground and printing dict["b"] confirms this by printing the string Optional(nil)

bobDevil
  • 27,758
  • 3
  • 32
  • 30
1

Assuming you are using the latest Xcode, 8.1 (8B62) with Swift 3.0.1 .

Any containing nil is not so easy as simple nested optional:

Nested Optional

var dictWithOptional: [String: AnyObject?] = [
    "a": "aa" as NSString,
    "b": nil,
]
if let b = dictWithOptional["b"] { //<-check if `dict` contains value for "b"
    print(b) //->nil (with compiletime warning)
    print(b == nil) //->true
}

b == nil returns true.

Any containing nil

var dict: [AnyHashable: Any]
let b: AnyObject? = nil
let c = b as Any
dict = ["a": "aa", "b": c]

print(Array(dict.keys)) //->[AnyHashable("b"), AnyHashable("a")]
if let b = dict["b"] { //<-check if `dict` contains value for "b"
    print(b) //->nil
    print(b == nil) //->false (with compiletime warning)
}

b == nil becomes false as written by the OP.

You can detect nil in Any, with something like this:

if let b = dict["b"] { //<-check if `dict` contains value for "B"
    if b as AnyObject === NSNull() {
        print("b is nil") //->b is nil
    }
}

(This works in Swift 3.0.1, not in Swift 3.0.0 .)

Community
  • 1
  • 1
OOPer
  • 47,149
  • 6
  • 107
  • 142