Section 1 · Swift the Language
Optionals Without Crashes
16 min3 min readSpot the bug
Lesson at a glance
Read optional types, unwrap them safely with if let, guard let and ??, and explain why a force unwrap is a crash waiting to happen.
- 1What an optional is
- 2if let: use the value in one branch
- 3guard let: exit early, keep the happy path flat
- 4?? and optional chaining
- 5The force unwrap
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
The user taps Add Expense, types “12.50” into the amount field, and taps Save. Your code receives a String. Turning it into a number can fail: the field might be empty, or the user might have typed “twelve”. Many languages hand you a NaN, a zero or an exception for that. Swift hands you an optional, and makes you deal with the missing case before the code compiles.
What an optional is
import Foundation
let typed = "12.50"
let amount = Decimal(string: typed) // amount is Decimal?, not DecimalThe question mark in Decimal? means “a Decimal, or nothing”. The “nothing” is spelled nil. Under the hood an optional is an enum with two cases, .some(value) and .none, which is why you can picture it as a box that is either full or empty.
An optional is not the value inside it. Try amount + 5 and the compiler stops you with “value of optional type ‘Decimal?’ must be unwrapped to a value of type ‘Decimal’”. That error is the feature. Every place a value might be missing is marked in the type, and Swift won’t let you forget it.
if let: use the value in one branch
if let checks the box and, when it’s full, gives you the value under a new non-optional name:
if let amount = Decimal(string: typed) {
print("Adding \(amount)") // amount is a Decimal here
} else {
print("That isn't a number")
}When the unwrapped name matches an existing optional, Swift 5.7 and later let you drop the right-hand side: if let amount { … }. The new amount exists only inside the braces, which is perfect when the value matters for one small branch.
guard let: exit early, keep the happy path flat
Most of the time, a missing value means “stop here”. guard let says exactly that. Here is the function Pocket Budget uses to validate the amount field:
import Foundation
func parseAmount(_ text: String) -> Decimal? {
let trimmed = text.trimmingCharacters(in: .whitespaces)
guard let amount = Decimal(string: trimmed), amount > 0 else {
return nil
}
return amount
}
print(parseAmount("12.50") as Any) // Optional(12.5)
print(parseAmount("") as Any) // nil
print(parseAmount("-3") as Any) // nilThe else block must leave the current scope with return, throw, break or continue, and the compiler enforces it. In exchange, amount stays available as a plain Decimal for the rest of the function. The comma adds a second condition, so one guard rejects both “not a number” and “zero or negative”. Code written this way reads top to bottom: checks first, real work after, no pyramid of nested ifs.
?? and optional chaining
Sometimes a missing value has an obvious default. The nil-coalescing operator ?? supplies it:
let note: String? = nil
let label = note ?? "No note" // "No note"
let noteLength = note?.count ?? 0 // 0note?.count is optional chaining: if note is nil, the whole expression becomes nil instead of crashing, and ?? turns that into 0. Chains can be long (expense?.category?.name), and they stop at the first nil.
Optionals are also a design decision, not only something functions hand you. In Pocket Budget an expense always has a title and an amount, so those are plain String and Decimal. A note is genuinely optional, so it’s String?. Resist making things optional “to be safe”: every ? you add is a question every reader of that value has to answer. Make a property optional only when “no value” is a real, meaningful state.
The force unwrap
There is one more way to open the box: !.
let amount = Decimal(string: typed)!If typed is “12.50”, this works. If the field is empty, your app dies with “Fatal error: Unexpectedly found nil while unwrapping an Optional value”. There’s no error message for the user and no chance to recover.
! has legitimate uses, such as URL(string: "https://example.com")! built from a literal you wrote yourself. There, nil would mean a typo in your code, and crashing during development is the fastest way to find it. Be ready to defend every ! in code review.
With optionals in hand you can handle one missing value safely. Next you’ll store many values at once in arrays, dictionaries and sets, and steer through them with loops and switch.
Key takeaways
- An optional such as
Decimal?either holds a value or holdsnil, and Swift won’t let you use it as a plain value until you unwrap it. - Use
guard letto unwrap at the top of a function and exit early, so the rest of the function works with a real value. - Use
if letwhen you only need the value inside one branch, and??when a sensible default exists. - A force unwrap (
!) crashes the app the moment the value isnil; treat every!as a claim you must be able to prove.