How to Use Optional Chaining in Swift
What you’ll build or solve
You’ll learn how to safely read properties and call methods on optional values using ?..
When this approach works best
Optional chaining works best when:
Learn Swift on Mimo
- You access nested model data that may be partially missing.
- You read optional properties returned from APIs.
- You call methods on optional instances.
- You work with collections that may or may not exist.
This is a bad idea if your logic requires the value to exist and you need to exit early. In that case, use guard let or if let instead.
Prerequisites
- Xcode installed
- Basic Swift syntax
- Understanding of optionals (
String?,Int?)
Step-by-step instructions
Step 1: Use ?. to access properties and methods safely
Optional chaining uses the ?. operator.
Property access
varusername:String?="Srdan"
letlength=username?.count
Method call
letuppercased=username?.uppercased()
If username is nil, both expressions return nil. No crash occurs.
Chaining multiple levels
structProfile {
varemail:String?
}
structUser {
varprofile:Profile?
}
varuser=User(profile:nil)
letemail=user.profile?.email
Swift evaluates from left to right. If profile is nil, evaluation stops and returns nil.
Works with collections
varscores: [String:Int]?= ["Alex":90]
letalexScore=scores?["Alex"]
varnumbers: [Int]?= [1,2,3]
letfirstNumber=numbers?.first
The same ?. operator applies. The result is always optional.
What to look for
- The result of optional chaining is always optional.
- Evaluation stops at the first
nil. - No runtime crash occurs if a value is missing.
You can use the result inside conditions:
ifuser.profile?.email!=nil {
print("Email exists")
}
Optional chaining only provides safe access. It does not unwrap to a non-optional value.
Examples you can copy
Example 1: Safe nested access
CSS
structAddress {
varcity:String?
}
structProfile {
varaddress:Address?
}
structUser {
varprofile:Profile?
}
letuser=User(profile:Profile(address:Address(city:"Vienna")))
letcity=user.profile?.address?.city
If any part of the chain is nil, city becomes nil.
Example 2: Conditional method call
classSession {
funcrefresh() {
print("Session refreshed")
}
}
varsession:Session?=Session()
session?.refresh()
If session exists, the method runs.
If session is nil, nothing happens.
Example 3: Working with dictionary values
varsettings: [String:String]?= [
"theme":"dark"
]
lettheme=settings?["theme"]
If settings is nil, theme is nil.
If the key does not exist, theme is also nil.
Optional chaining handles both cases safely.
Common mistakes and how to fix them
Mistake 1: Expecting a non-optional result
What you might do:
varusername:String?="Srdan"
letlength:Int=username?.count
Why it breaks:
username?.count returns Int?, not Int.
Correct approach:
letlength=username?.count??0
Or unwrap safely:
ifletlength=username?.count {
print(length)
}
Mistake 2: Force unwrapping after chaining
What you might do:
letlength=username?.count!
Why it breaks:
If username is nil, you reintroduce crash risk.
Correct approach:
letlength=username?.count
Then handle the optional properly:
ifletlength {
print(length)
}
Troubleshooting
If you see “Value of optional type must be unwrapped,” remember that ?. still returns an optional.
If a chained expression always returns nil, check whether an earlier property in the chain is nil.
If a method does not run, confirm the object itself is not nil.
If a conditional does not behave as expected, print the chained value to inspect it.
Quick recap
- Use
?.to safely access properties and methods. - The result of optional chaining is always optional.
- Evaluation stops at the first
nil. - Works the same for properties, methods, and collections.
- Does not crash if the value is missing.
- Unwrap or provide defaults after chaining when needed.
Join 35M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot