Two (or more) optionals in Swift

(Updated for Swift >=3)

“Double optionals” can be useful, and the Swift blog entry “Optionals Case Study: valuesForKeys” describes an application.

Here is a simplified example:

let dict : [String : String?] = ["a" : "foo" , "b" : nil]

is a dictionary with optional strings as values. Therefore

let val = dict[key]

has the type String?? aka Optional<Optional<String>>. It is .none (or nil)
if the key is not present in the dictionary, and .some(x) otherwise. In the second
case, x is a String? aka Optional<String> and can be .none (or nil)
or .some(s) where s is a String.

You can use nested optional binding to check for the various cases:

for key in ["a", "b", "c"] {

    let val = dict[key]
    if let x = val {
        if let s = x {
            print("\(key): \(s)")
        } else {
            print("\(key): nil")
        }
    } else {
        print("\(key): not present")
    }

}

Output:

a: foo
b: nil
c: not present

It might be instructive to see how the same can be achieved with pattern matching
in a switch-statement:

let val = dict[key]
switch val {
case .some(.some(let s)):
    print("\(key): \(s)")
case .some(.none):
    print("\(key): nil")
case .none:
    print("\(key): not present")
}

or, using the x? pattern as a synonym for .some(x):

let val = dict[key]
switch val {
case let (s??):
    print("\(key): \(s)")
case let (s?):
    print("\(key): nil")
case nil:
    print("\(key): not present")
}

(I do not know a sensible application for more deeply nested optionals.)

Leave a Comment