Optional Chaining and Optional Patterns
Prerequisites: Unwrapping optionals safely, Structs
Real data is often nested, with optionals at several levels. A few tools keep that code short.
Optional chaining
?. calls a property or method only if the value before it isn't nil.
If any link in the chain is nil, the whole expression is nil:
struct Address { let city: String }
struct Company { let address: Address? }
struct Person { let name: String; let employer: Company? }
let ada = Person(name: "Ada", employer: Company(address: Address(city: "London")))
let sam = Person(name: "Sam", employer: nil)
print(ada.employer?.address?.city ?? "unknown")
print(sam.employer?.address?.city ?? "unknown")
print(sam.employer?.address?.city.count ?? 0)
London
unknown
0
The result of a chain is always optional, so it's usually combined with
?? or if let.
Unwrapping several at once
if let and guard let can unwrap several values, and mix in
conditions, separated by commas. Everything must succeed:
func shippingLabel(name: String?, city: String?, zip: String?) -> String {
guard let name, let city, let zip, zip.count == 5 else {
return "Incomplete address"
}
return "\(name), \(city) \(zip)"
}
print(shippingLabel(name: "Ada", city: "Boston", zip: "02101"))
print(shippingLabel(name: "Ada", city: nil, zip: "02101"))
Ada, Boston 02101
Incomplete address
Transforming an optional with map
map on an optional transforms the value if there is one, and keeps
nil otherwise:
let input: String? = "42"
let doubled = input.flatMap { Int($0) }.map { $0 * 2 }
print(doubled ?? 0)
84
flatMap is used here because Int($0) itself returns an optional —
flatMap avoids ending up with an optional inside an optional.
Common mistakes
- Force-unwrapping in the middle of a chain (
a!.b!.c). Onenilanywhere crashes the program. Use?.and handlenilonce at the end.