10. Optionals¶
What & why¶
Some values legitimately might not exist: a dictionary lookup that finds nothing, a string that doesn't parse as a number, the first element of an empty array. Many languages use null for this and let it crash you later. Swift makes absence part of the type: Int always holds an integer; Int? holds either an integer or nothing. The compiler forces you to handle the "nothing" case before you can use the value.
This is Swift's most important safety feature. Learn it well.
Declaring optionals¶
var nickname: String? = nil // no value
nickname = "Ace" // has a value
let number = Int("123") // Int? — Optional(123)
let bad = Int("abc") // Int? — nil
String? is shorthand for Optional<String>, which is just an enum with two cases: .none (written nil) and .some(value).
You can't use an optional like a regular value:
Unwrapping, safest first¶
1. if let — use it only if it exists¶
if let n = Int("42") {
print("Parsed \(n)") // n is a plain Int in here
} else {
print("Not a number")
}
// Shorthand when the new name equals the old one (Swift 5.7+):
let age: Int? = 30
if let age {
print("Age \(age)")
}
Combine several in one if:
2. guard let — exit early, keep the value afterwards¶
func parseAge(_ text: String) -> Int? {
guard let value = Int(text) else {
return nil
}
guard value >= 0 else { return nil }
return value // `value` is available for the rest of the function
}
guard let is the best choice when "no value" means "stop here."
3. ?? — nil-coalescing: provide a default¶
let input: String? = nil
let name = input ?? "Anonymous" // "Anonymous"
let port = Int("abc") ?? 8080 // 8080
4. Optional chaining ?. — keep going, or become nil¶
let text: String? = "swift"
print(text?.uppercased()) // Optional("SWIFT")
print(text?.count ?? 0) // 5
let missing: String? = nil
print(missing?.uppercased()) // nil — the whole chain short-circuits
A chain's result is always optional. Chains can be as long as you like: user?.address?.city?.count.
5. map and flatMap on optionals¶
let length = text.map { $0.count } // Int? (nil if text is nil)
let parsed = Optional("12").flatMap { Int($0) } // Int? (flatMap avoids Int??)
6. Force unwrapping ! — last resort¶
Only use ! when you can prove it can't be nil (for example, a hard-coded literal you control, or right after checking). If you're not sure, use one of the safe forms above. A crash in production is a much worse failure than writing two more lines.
Similarly, let x = y! on a user-supplied value is a bug waiting to happen.
Implicitly unwrapped optionals (Type!)¶
var label: String! is an optional that auto-unwraps when used. You'll see it in older UI code (outlets set after init). Avoid it in your own code.
Optional pattern matching¶
let score: Int? = 87
switch score {
case .some(let s) where s >= 90: print("great")
case .some(let s): print("score \(s)")
case .none: print("no score")
}
// Equivalent shorthand:
switch score {
case let s?: print("has \(s)")
case nil: print("none")
}
Optionals and collections¶
let names = ["Ada", "Grace"]
print(names.first ?? "nobody") // "Ada"
print(names.max() ?? "") // "Grace"
let strings = ["1", "two", "3"]
let numbers = strings.compactMap { Int($0) } // [1, 3] — nil results are discarded
let dict = ["a": 1]
print(dict["z"] ?? 0) // 0
Optionals as return values: designing APIs¶
Return an optional when absence is normal and expected ("no such user"). If something has gone wrong and the caller should know why, use throws (next lesson) instead.
func firstNegative(in numbers: [Int]) -> Int? {
for n in numbers where n < 0 { return n }
return nil
}
Comparing optionals¶
let a: Int? = 5
print(a == 5) // true — Swift wraps the 5 for you
print(a == nil) // false
print(a! > 3) // use unwrap first for ordering comparisons
print(a ?? 0 > 3) // true
== works if the wrapped type is Equatable.
Common mistakes¶
- Reaching for
!to silence the compiler. Pickif let,guard let, or??instead. - Double optionals (
Int??) frommapwhereflatMapwas wanted. - Shadowing confusion:
if let name = nameis fine and idiomatic; just remember the innernameis the unwrapped value. - Using an optional where a default would do — don't make something optional "just in case."
- Printing optionals by accident:
print(Int("5"))showsOptional(5). Unwrap first. - Using
nilwith non-optional types:var x: Int = nildoesn't compile.
Exercises¶
- Write
func safeDivide(_ a: Int, _ b: Int) -> Int?returningnilwhenb == 0. Call it withif let. - Given
["10", "x", "30"], produce the sum of the valid numbers usingcompactMapandreduce. - Write
func initials(first: String?, last: String?) -> Stringthat handles any combination of nil values. - Given a dictionary
[String: [Int]], print the maximum ofscores["ada"]or "no scores" using optional chaining and??. - Rewrite this using
guard let: nest 3if letlevels that parse width, height, and depth from strings, then multiply them. - Explain, in one sentence each, when you'd use
??,if let,guard let,?., and!.
Recap¶
T?means "aTornil"; the compiler forces you to deal withnil.- Unwrap with
if let/guard let, supply defaults with??, chain with?.. map/flatMap/compactMapwork with optionals and collections of optionals.!is a crash waiting to happen — use it only whennilis truly impossible.- Return optionals for "normal absence"; use
throwsfor failure with a reason.
Next: Files & error handling →