Unwrapping Optionals Safely
Prerequisites: Optionals
There are several ways to get the value out of an optional. Choosing the right one is a daily Swift skill.
if let — use it if it's there
let input = "42"
if let number = Int(input) {
print("You entered \(number)")
} else {
print("That's not a number")
}
You entered 42
Inside the braces, number is a plain Int. When unwrapping a
variable into a constant with the same name, Swift 5.7+ has a
shorthand:
let nickname: String? = "Addy"
if let nickname {
print("Hi, \(nickname)")
}
Hi, Addy
guard let — exit early if it's missing
guard checks a condition and must leave the current scope
(usually with return) if it fails. After the guard, the unwrapped
value is available for the rest of the function:
func greet(_ name: String?) {
guard let name else {
print("No name given")
return
}
print("Hello, \(name)!")
}
greet("Ada")
greet(nil)
Hello, Ada!
No name given
guard keeps the main code un-indented: handle the problem cases at
the top, then write the normal path. (Functions have their own lesson
shortly.)
?? — provide a default
The nil-coalescing operator gives a fallback value:
let savedName: String? = nil
let name = savedName ?? "Guest"
print(name)
let quantity = Int("abc") ?? 1
print(quantity)
Guest
1
! — force unwrap (use sparingly)
! takes the value out without checking. If the optional is nil,
the program crashes:
let value = Int("forty")!
Fatal error: Unexpectedly found nil while unwrapping an Optional value
Only force unwrap when nil is truly impossible and a crash would mean
a programming error — for example, URL(string: "https://swift.org")!
with a hard-coded, known-good address.
Which to use
| Situation | Use |
|---|---|
| Do something only if the value exists | if let |
| Can't continue without the value | guard let |
| A sensible default exists | ?? |
nil would be a bug in your own code |
! |
Practice
Write a function parseAge(_ text: String) -> Int that returns the age
if text is a valid number, or 0 otherwise. Call it with "36" and
"abc".
Show solution
func parseAge(_ text: String) -> Int {
Int(text) ?? 0
}
print(parseAge("36"))
print(parseAge("abc"))
36
0