What are optionals in Swift, and how do you unwrap them safely?
TL;DR
An optional is a type that can hold a value or nil. You write it as String?.
You must unwrap it before you use the value.
Safe ways: if let, guard let, ??, optional chaining ?., and map. Avoid ! unless a nil is truly a programmer error.
An optional is just an enum
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
let name: String? = "Ana" // same as .some("Ana")
let none: String? = nil // same as .noneKnowing this helps in interviews. It explains why you can switch over an optional.
switch name {
case .some(let value): print("Hi \(value)")
case .none: print("No name")
}if let — use the value only inside a block
let input: String? = "42"
if let input, let number = Int(input) {
print(number + 1) // 43
} else {
print("Not a number")
}Since Swift 5.7 you can write if let input instead of if let input = input.
guard let — exit early, keep the value after
func greet(_ user: User?) {
guard let user else {
print("No user")
return
}
print("Hello \(user.name)") // user is non-optional here
}Use guard when nil means "stop". It keeps the happy path flat, not nested.
?? — give a default
let title: String? = nil
let label = title ?? "Untitled" // "Untitled"Optional chaining ?.
Call a property or method only if the value exists. The result is always optional.
let count = user?.address?.city.count // Int?If any link is nil, the whole chain is nil. No crash.
map and flatMap
let text: String? = "5"
let doubled = text.flatMap { Int($0) }.map { $0 * 2 } // Optional(10)map: transform the value if it exists.flatMap: transform with a function that itself returns an optional. AvoidsInt??.
Force unwrap ! — use with care
let url = URL(string: "https://example.com")!This crashes if the value is nil. It is fine for a hard-coded constant you know is valid. Do not use it on user input or network data.
Implicitly unwrapped optionals T!
@IBOutlet weak var titleLabel: UILabel!It is still an optional, but Swift unwraps it for you on use. If it is nil, the app crashes. You mostly see it with @IBOutlet, because the outlet is set after init.
Comparison
| Tool | When to use |
|---|---|
if let | Do something only when a value exists |
guard let | Need the value for the rest of the function |
?? | A sensible default exists |
?. | Reach into nested optionals |
! | nil would be a bug, and you want a crash |
Common mistakes
- Force unwrapping data from JSON or user input.
- Deep nesting of
if let. Useguardor combine them with commas. - Comparing to
niland then force unwrapping. Useif letinstead.
// Bad
if user != nil { print(user!.name) }
// Good
if let user { print(user.name) }Common follow-up questions
- What is the difference between
nilin Swift and Objective-C? In Objective-C,nilis only for objects (a null pointer). In Swift, any type can be optional, includingIntand structs. - What does
try?return? An optional. It isnilif the function throws. - What is a double optional?
Int??. You get it from things likedictionary[key]when the value type is itself optional. UseflatMapor pattern matching to flatten it. - Can you use optionals in a
forloop? Yes, withfor case let x? in arrayto skipnilvalues. Or usecompactMap. - Why does Swift have optionals at all? To make "no value" explicit in the type system, so the compiler forces you to handle it.