Optional Chaining

Optional chaining is the process of querying and calling into an optional that might currently be nil. If the optional has a value the call succeeds; if it's nil the whole expression gracefully returns nil instead of crashing. It's the safe, expressive alternative to forced unwrapping, and it scales cleanly across many levels of nested optionals.

Optional chaining as an alternative to forced unwrapping

You write optional chaining by placing a question mark (?) after the optional value you want to reach through. This looks like the exclamation mark used for forced unwrapping (!), but with a crucial difference: where ! triggers a runtime crash if the value is nil, ? simply yields nil.

Because the access might fail, the result of optional chaining is always optional — even when the property you're reading isn't. If you chain to an Int property, you get back an Int?. This lets you check for success with optional binding.

Defining model classes

We'll use this small model — a person who may have a residence, which may have rooms and an address:

class Person {
    var residence: Residence?
}

class Residence {
    var rooms: [Room] = []
    var numberOfRooms: Int { rooms.count }
    var address: Address?

    subscript(i: Int) -> Room {
        get { rooms[i] }
        set { rooms[i] = newValue }
    }
    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }
}

class Room {
    let name: String
    init(name: String) { self.name = name }
}

class Address {
    var street: String?
    func buildingIdentifier() -> String? { street }
}

Accessing properties through optional chaining

A new Person has a nil residence. Using ! would crash; using ? safely returns nil:

let john = Person()

// let count = john.residence!.numberOfRooms  // 💥 crash: residence is nil

if let count = john.residence?.numberOfRooms {
    print("John has \(count) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// → Unable to retrieve the number of rooms.

You can also set a property through a chain. The assignment simply does nothing if any link is nil:

let someAddress = Address()
someAddress.street = "Laurel Street"
john.residence?.address = someAddress   // no effect — residence is nil

Calling methods through optional chaining

You can call a method on an optional too. Even a method that returns Void (like printNumberOfRooms()) returns Void? when called through a chain, so you can check whether the call actually happened:

if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}
// → It was not possible to print the number of rooms.

Accessing subscripts through optional chaining

To reach a subscript on an optional, place the ? before the brackets — it belongs to the optional value, not the subscript:

if let firstRoomName = john.residence?[0].name {
    print("The first room is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// → Unable to retrieve the first room name.

// Subscripts of optional collections work the same way:
var scores = ["Dave": [86, 82], "Bev": [79, 94]]
scores["Dave"]?[0] = 91     // Dave exists → updates to 91
scores["Bev"]?[0] += 1      // Bev exists → 80
scores["Kim"]?[0] = 72      // Kim absent → no effect

Linking multiple levels of chaining

You can stitch chains together to drill several levels deep. Two rules govern the result:

So reaching a non-optional String several levels down still yields just one String?:

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// → Unable to retrieve the address.

Give John a residence and an address, and the same chain succeeds:

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
let johnsAddress = Address()
johnsAddress.street = "Acacia Avenue"
johnsHouse.address = johnsAddress
john.residence = johnsHouse

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
}
// → John's street name is Acacia Avenue.

Chaining on methods with optional return values

You can keep chaining onto the result of a method that itself returns an optional. Here buildingIdentifier() returns String?, and we chain further to call a method on that result. The ? goes after the method's parentheses, since it is the method's return value we're chaining through:

if let beginsWithThe =
    john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
    if beginsWithThe {
        print("John's building identifier begins with \"The\".")
    } else {
        print("John's building identifier does not begin with \"The\".")
    }
}
// → John's building identifier does not begin with "The".

Tip: Optional chaining pairs naturally with the nil-coalescing operator ?? to supply a fallback in one line: let rooms = john.residence?.numberOfRooms ?? 0.

Wrapping up

Optional chaining lets you write expressive code that reaches deep into optional values without a ladder of if let checks or risky forced unwraps. Remember the core guarantee: the moment any link in the chain is nil, the whole expression short-circuits to nil — and the result is always an optional. Next we tackle error handling, Swift's structured way to respond when an operation can fail.