Optional Chaining Oddities
24 Oct 2015

Optional chaining in Swift is a means to safely access properties of potential nil values using the ?. operator. This concept is also known in other languages, particularly in Groovy or Spring EL which provide the «safe navigation operator». However, there is a subtle difference between Groovy's and Swift's implementation.

To recap shortly: Swift has the concept of optional types. An optional variable or property might be nil (it has no value). In contrast, non-optional types must have a value. Always. The compiler will check that you follow this rule. The concept itself is great (in Java for example there is no language construct to ensure that certain reference types will never become null). Optional types in Swift are marked with a question mark ? after the type.

When accessing the value of an optional type one has to manage explicitly the case that it might be nil (using opt != nil checks or optional binding if let opt = opt ... ).

Or you can use Optional Chaining. You put a question mark between an optional value and a property, method, or subscript access. Just like this

let foo1 = bar?.prop
let foo2 = bar?.method()
let foo3 = bar?[0]

If bar appears to be nil then all of the foos will also be nil. The results will always be optional values, even if the property prop or the method method() are declared as non-optional for bar. No surprises so far.

OK, let's consider these definitions (see also the examples in the original Swift documentation):

class Person {
    var residence: Residence = Residence()
}

class Residence {
    var rooms = [Room]()
}

class Room {
    // ...
}

Now suppose you have an optional person named Mary:

let mary: Person? = ...

Then you can safely access Mary's residence as follows (I have added the type declarations in the following snippets):

let marysFlat: Residence? = mary?.residence

So marysFlat is an optional value again. Accessing the rooms array then can be done with

let rooms: [Room]? = marysFlat?.rooms

So far so good, but what if you want to access Mary's rooms with one property access chain? I have tried the following (as it would be correct in Groovy):

let rooms: [Room]? = mary?.residence?.rooms

I have got an error stating

Cannot use optional chaining on non-optional value of type 'Residence'

Wait, what? Wasn't mary?.residence supposed to be an optional value? Funnily enough, the following works

let rooms: [Room]? = (mary?.residence)?.rooms

It turns out that the correct expression (or better: property access chain) must be:

let rooms: [Room]? = mary?.residence.rooms

No question mark between residence and .rooms. Indeed, the property residence in Person is non-optional (and if you have a person then it will always have a non-nil residence). Apparently the two expressions (mary?.residence)?.rooms and mary?.residence?.rooms don't represent the same data structures. And the Swift compiler will in a way cut the evaluation of the property chain short. I am not quite sure yet which concept seems to be more intuitive: the Groovy way or the Swift way.

By the way: Apparently some Groovy developers made the same error in reasoning, but in the other direction.

I'm interested in your feedback. Send me a mail or ping me on twitter.