This post originally appeared on Medium.

My unit test failed. It wanted a nil but got a <nil>. My initial reaction was to accuse my computer of being dumb. Are you expecting a different type of nil from me? There’s only one nil, and its name is nil.

But hold up. Computers aren’t sophisticated enough to prank us. What did I do wrong?

The Dictionary Problem

var dict: [String: Any?] = ["first": nil, "second": 2]

if let val = dict["first"] {
    print(val)
}
// Output: nil

dict["first"] = nil
if let val = dict["first"] {
    print(val)
}
// No output

It’s standard in Swift to unwrap an optional with if let. Why did I receive a nil on line 3?

There’s a clue on line 8. When I explicitly set first to be nil, the unwrap operation behaves as expected. There really must be a different kind of nil here.

How Dictionary fetches values

Dictionary wraps the value in an optional before handing it to you.

This is all good and expected. My problem came in because I was setting my value type as Any? instead of Any. Swift was double wrapping my value!

Setting a key to nil

When you set a key to nil in a Dictionary it removes that key/value pair for you. That’s why line 8 above behaves as expected.

If you want to explicitly set your value to nil in a Dictionary with an optional value type, you must wrap it yourself first.

dict["first"] = .some(nil)
if let val = dict["first"] {
    print(val)
}
// prints ‘nil’

Swift was doing this extra step automatically when I first created my dictionary with var dict: [String: Any?] = ["first", nil] — it wrapped that nil for me behind the scenes.

Don’t do this!

I ended up refactoring my Dictionary to not use an optional for its value. The fewer quirky rules one has to keep in mind the better. If I got confused from my own code, what would happen to another developer jumping in? For a longer meditation on this idea, check out Tyler Johnson’s essay on code design.

Code should present itself like a political cartoon — full of blunt metaphors with helpful labels and no subtlety.

Sean judges what code looks like at Livefront.