Swift from Zero to Expert #11: Optionals & Optional Chaining
Swift's antidote to the billion-dollar mistake. Optional is just an enum — and nil, if let, guard let, ??, !, and optional chaining all compile down to checking which case you're in.
In the previous article we walked a class from birth to death — init guaranteeing a valid object, deinit handing back what it borrowed. We even saw a failable initializer (init?) hand you back an optional instance, and promised the full story here. This is that story.
Optionals are Swift’s answer to the billion-dollar mistake — Tony Hoare’s name for the null reference he invented in 1965, which has caused untold crashes ever since. Swift’s fix isn’t a clever runtime trick. It’s a type. A value of type Int is always an integer. A value of type Int? is either an integer or nothing — and the type system won’t let you forget the second possibility.
An optional isn’t a special language feature bolted onto types. It’s just an enum with two cases — one that holds a value, one that doesn’t. Everything else is syntax over that single idea.
nil: the absence of a value
You’ve already met optionals without naming them. A failable initializer returns one:
let possibleNumber = "123"let convertedNumber = Int(possibleNumber)// convertedNumber is of type "optional Int", or "Int?"Int(_:) can’t promise success — "123" converts cleanly, but "hello" doesn’t — so it returns an Int? rather than an Int. To represent “no value,” set an optional to nil:
var serverResponseCode: Int? = 404serverResponseCode = nil// serverResponseCode now contains no valueIf you declare an optional variable without giving it a value, it’s automatically set to nil:
var surveyAnswer: String?// surveyAnswer is automatically set to nilUnder the hood: Optional is an enum
Here’s the part the series exists for. Optional is not magic syntax — it’s an ordinary generic enum in the standard library. <Wrapped> is a placeholder type that gets filled in with whatever you wrap — Int? is Optional<Int>, so Wrapped becomes Int. (Generics get their own article later; here you only need that Optional<Int> means “an Optional holding an Int”.) Its declaration is essentially:
enum Optional<Wrapped> { case none // the absence of a value — this is what `nil` means case some(Wrapped) // the presence of a value, stored as Wrapped}The ? you write is pure sugar: Int? is exactly Optional<Int>. nil is exactly Optional.none. Assigning a real value wraps it in .some. The two forms are interchangeable:
let shortForm: Int? = Int("42")let longForm: Optional<Int> = Int("42")
let number: Int? = Optional.some(42)let noNumber: Int? = Optional.noneprint(noNumber == nil) // trueOnce you see this, every operator in this article stops being mysterious. Unwrapping an optional is literally the enum pattern matching from #6 — asking “am I in the .some case, and if so, what’s the associated value?” if let, guard let, ??, and ! are all just ergonomic ways to ask that one question.

Forced unwrapping: the ! that can crash
If you’re certain an optional holds a value, you can reach in and grab it with a trailing exclamation mark — forced unwrapping:
let possibleNumber = "123"let convertedNumber = Int(possibleNumber)
if convertedNumber != nil { print("convertedNumber has an integer value of \(convertedNumber!).")}// convertedNumber has an integer value of 123.convertedNumber! says “I know this isn’t nil — give me the Int inside.” In enum terms, it asserts you’re in the .some case and returns the associated value.
Optional binding: if let and guard let
The if let form unwraps into a new constant that’s only in scope inside the if body:
let possibleNumber = "123"
if let actualNumber = Int(possibleNumber) { print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")} else { print("The string \"\(possibleNumber)\" couldn't be converted to an integer")}// The string "123" has an integer value of 123If Int(possibleNumber) is .some(value), actualNumber is bound to that value and the if branch runs. If it’s .none, the else branch runs. The unwrapped actualNumber is a plain Int — no ?, no !.
When the unwrapped constant would share the optional’s name, you can use the shorthand: just write if let name with no =:
if let convertedNumber { // convertedNumber is now a non-optional Int, named the same as the optional print(convertedNumber)}You can bind several optionals (and add Boolean clauses) in one if, separated by commas. The whole thing succeeds only if every binding succeeds:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 { print("\(firstNumber) < \(secondNumber) < 100")}// 4 < 42 < 100guard let: bind for the rest of the scope
if let binds inside its own little block. Often you want the opposite — unwrap once at the top of a function and use the value through the rest of the body. That’s guard let:
func greet(_ name: String?) { guard let name else { print("No name provided") return } // name is a non-optional String from here to the end of the function print("Hello, \(name)!")}A guard requires its condition to be true to continue. If the binding fails, the else block runs and must exit the current scope (return, break, throw, etc.). The payoff: the unwrapped name stays in scope for everything after the guard, with no extra indentation. This is the early-exit pattern from #5, applied to optionals.
The ?? nil-coalescing operator
let defaultColorName = "red"var userDefinedColorName: String? // defaults to nil
var colorNameToUse = userDefinedColorName ?? defaultColorName// userDefinedColorName is nil, so colorNameToUse is set to "red"The right-hand side b must match the type of the unwrapped a. Here userDefinedColorName is String? and defaultColorName is String, so the result is a non-optional String.
Because ?? can take another optional on its right, you can chain it to express “try this, then that, then fall back”:
let imagePaths = ["star": "/glyphs/star.png"]let defaultImagePath = "/images/default.png"
let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePathprint(shapePath)// /images/default.png — both lookups failed, so the final default winsImplicitly unwrapped optionals
The motivating case: a value that’s nil for a brief window during setup, then permanently non-nil afterward. Writing ? and unwrapping it on every single access would be pure noise.
let possibleString: String? = "An optional string."let forcedString: String = possibleString! // requires an explicit !
let assumedString: String! = "An implicitly unwrapped optional string."let implicitString: String = assumedString // no ! needed — unwrapped automaticallyYou can still treat an implicitly unwrapped optional like a normal optional — check it against nil, bind it with if let. But every plain use of it forces an unwrap behind the scenes:
if assumedString != nil { print(assumedString!)}
if let definiteString = assumedString { print(definiteString)}Optional chaining: querying through nil
We’ve spent the chapter unwrapping a single optional. But real models nest — a Person has an optional Residence, which has an optional Address, which has an optional street. Optional chaining lets you reach down through all of them in one expression, failing gracefully the moment any link is nil.
Optional chaining is the safe sibling of forced unwrapping. Where ! traps on nil, ? returns nil. Start with two simple classes:
class Person { var residence: Residence?}
class Residence { var numberOfRooms = 1}A fresh Person has residence == nil (optionals default to nil). Forcing through it crashes:
let john = Person()let roomCount = john.residence!.numberOfRooms// this triggers a runtime error — residence is nilSwap the ! for ? and the call fails gracefully instead, returning nil:
if let roomCount = john.residence?.numberOfRooms { print("John's residence has \(roomCount) room(s).")} else { print("Unable to retrieve the number of rooms.")}// Unable to retrieve the number of rooms.
Chaining multiple levels deep
You can chain as deep as your model goes. Add an Address with an optional street:
class Residence { var rooms: [Room] = [] var numberOfRooms: Int { rooms.count } subscript(i: Int) -> Room { rooms[i] } var address: Address?}
class Room { let name: String init(name: String) { self.name = name }}
class Address { var street: String?}Now reach through two optional links at once:
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.Chaining on subscripts and methods
The ? goes before a subscript’s brackets — it always follows immediately after the optional part of the expression:
if let firstRoomName = john.residence?[0].name { print("The first room name is \(firstRoomName).")} else { print("Unable to retrieve the first room name.")}// Unable to retrieve the first room name.Calling a method through a chain works even for methods that return nothing. A Void-returning method becomes Void? through a chain — and comparing that against nil tells you 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.The same trick works for setting through a chain. The assignment returns Void?; if the chain was nil, nothing on the right-hand side is even evaluated:
let someAddress = Address()john.residence?.address = someAddress// residence is nil, so someAddress is never assigned — and the chain silently no-opsForced unwrapping says “this is never nil — crash if I’m wrong.” Optional chaining says “this might be nil — and that’s fine, just give me nil back.” Same ?/! duality, all the way down.
Recap

- Optional — a value that’s either present (
.some) or absent (.none/nil); writtenT?, which is exactlyOptional<T> - Under the hood —
Optionalis just an enum with two cases; every unwrap is enum pattern matching - nil — the absence of a value, not a null pointer; only optionals can be
nil - Forced unwrapping (
!) — grabs the value but traps ifnil; a promise you can break - Optional binding (
if let/guard let) — safely unwrap into a non-optional constant;guard letkeeps it in scope for the rest of the body - Nil-coalescing (
??) — unwrap-or-default; the default is lazy (@autoclosure) and short-circuits - Implicitly unwrapped (
T!) — an optional unwrapped automatically on every use; still crashes ifnil - Optional chaining (
?) — query through nested optionals; the result is always optional and the chain flattens and fails gracefully
What’s next
In the next article we explore Error Handling — throws, try, do/catch, and Result. We’ll see how Swift draws a sharp line between an expected absence of a value (an optional) and an expected failure with a reason (a thrown error), and why try? quietly turns one into the other.
See you next week.
The billion-dollar mistake wasn’t null itself — it was making every reference nullable by default. Swift’s fix is to make absence visible in the type. Once you internalize “an optional is just an enum,” nil stops being scary and starts being honest.
References
Related
-
- swift
- swift-zero-expert
- swift-fundamentals
Swift from Zero to Expert #10: Inheritance & Initialization
Subclassing, overriding, and super. Designated vs convenience initializers, two-phase initialization, failable and required init, and deinit — the full lifecycle of a class, explained in memory.
-
- swift
- swift-zero-expert
- swift-fundamentals
Swift Zero to Expert #9: Properties, methods, and subscripts
Stored vs computed properties, observers, lazy, static. How properties define the memory layout and why computed = zero storage.
-
- swift
- swift-zero-expert
- swift-fundamentals
Swift Zero to Expert #8: Structs vs Classes — the decision that shapes your app
Value semantics vs reference semantics, static vs dynamic dispatch, and why Apple recommends structs by default. The article that changes how you think about Swift.