A setting may be missing even when the rest of an object is present. Perhaps a user has never chosen a display name, or a saved language is unavailable. We can handle that with an if let and a separate branch. But when both paths should produce one value, Swift gives us a smaller expression: ??.
The nil-coalescing operator picks the value inside an optional when it exists. Otherwise, it picks a fallback. That sounds like simple syntax, yet it makes a useful promise about when the fallback runs, and it has an important boundary: missing is not the same as empty, false, or invalid.
Choose a value at the point of use
Imagine we want a label for an account that may not contain a name:
let name: String? = nil
let displayName = name ?? "Guest"
print(displayName) // Guest
let savedName: String? = "Mira"
print(savedName ?? "Guest") // Mira
Both expressions have an optional String? on the left and a plain String fallback on the right. When name is nil, displayName becomes "Guest". The second example shows the other path: savedName contains "Mira", so the fallback is ignored and the output is Mira. Both results are non-optional strings; no further unwrap is needed.
Neither expression writes a default back into the original optional. It only chooses a value at the point of use, which is different from updating a stored setting.
The operator is not the same as “use the fallback if the value looks empty.” Consider two more settings:
let notificationsEnabled: Bool? = false
let label: String? = ""
print(notificationsEnabled ?? true) // false
print((label ?? "Untitled").isEmpty) // true
Both values are present. The first line prints false, not true. The second prints true because the resulting string is still empty: the fallback "Untitled" was not chosen. A saved false and an empty string are not nil, even if the application considers them unusable.
The fallback waits until it is needed
There is another behavior worth knowing before putting a function call on the right of ??: Swift does not evaluate that expression when the optional already contains a value. The Swift Book calls this short-circuit evaluation.
func makeDefault() -> String {
print("Building the default")
return "Generated"
}
let saved: String? = "Cached"
print(saved ?? makeDefault()) // Cached
let missing: String? = nil
print(missing ?? makeDefault())
// Building the default
// Generated
For saved, only Cached is printed: Swift never calls makeDefault(). For missing, the function does run, printing Building the default before the final result Generated. Both paths are visible without changing the example.
This is more than a small optimization. A default might load a configuration, create a value, log an event, or perform some other work. With ??, that work belongs only to the missing-value path. If the fallback should run regardless, compute it before the expression instead of placing that work inside ??.
A final default after an optional chain
In the previous Molecule, optional chaining let a read travel through several values that might be missing. Nil coalescing makes a natural final step when the caller needs a non-optional result.
struct Preferences { let language: String? }
struct Account { let preferences: Preferences? }
let account: Account? = Account(
preferences: Preferences(language: "uk")
)
let missingAccount: Account? = nil
let missingPreferences: Account? = Account(preferences: nil)
let missingLanguage: Account? = Account(
preferences: Preferences(language: nil)
)
print(account?.preferences?.language ?? "en") // uk
print(missingAccount?.preferences?.language ?? "en") // en
print(missingPreferences?.preferences?.language ?? "en") // en
print(missingLanguage?.preferences?.language ?? "en") // en
The first result is uk: every link exists. The three other results are en, but for different reasons: the account itself is missing, its preferences are missing, or its language is missing. The fallback tells us which language to use, not which link was missing. For a display preference, that may be enough.
Poof! Let’s rewind...
#005 — Optional Chaining: Let Absence Flow Through an Expression showed how ?. can stop a read when one link is missing. ?? is a useful next step: keep the absence while reading, then choose a single value where the caller needs one.
Reading a dictionary is not inserting a default
Dictionary lookups also return optionals, because a key may not exist. That makes ?? useful for a read that should treat a missing count as zero:
var visits = ["Mira": 3]
let count = visits["Noah"] ?? 0
print(count) // 0
print(visits["Noah"] == nil) // true
The code prints 0 and then true: the key "Noah" is still missing after the read. The zero is a local result, not a new dictionary entry. If we actually want to create or update a count, Swift has a different tool:
visits["Noah", default: 0] += 1
print(visits["Noah"] ?? 0) // 1
Here the dictionary’s defaulted subscript supplies an initial value for a mutation. These expressions may both contain a zero, but their jobs are different. Use ?? 0 when reading with a fallback is enough; use [key, default: 0] when the operation should update stored data.
A default cannot tell you why a value is missing
Suppose a text field contains a number that we want to convert:
let invalid = "many"
let validZero = "0"
print(Int(invalid) ?? 0) // 0
print(Int(validZero) ?? 0) // 0
print(Int(invalid) == nil) // true
print(Int(validZero) == nil) // false
if let number = Int(invalid) {
print("Accepted:", number)
} else {
print("Invalid input:", invalid) // Invalid input: many
}
The first two lines both print 0, yet the next two reveal the difference: "many" fails to parse (true for the nil check), while "0" is a valid integer (false). Nil coalescing quietly turns failed conversion into zero, even though the two inputs mean different things. The operator did what we asked; the product decision may still be wrong.
The if let branch in the example handles the invalid text explicitly: it prints Invalid input: many. If a caller requires early exit, guard let can express the same validation decision. If the caller explicitly defines a missing count as zero, ?? 0 can be the right choice. The key question is not how to avoid a branch, but whether missing data and a real default should mean the same thing.
The same distinction applies to empty strings, empty collections, and false. Swift checks whether the optional has a value. Only application logic can decide whether that value is acceptable.
More than one possible fallback
A chain of ?? expressions can be helpful when several sources have a clear preference order:
let preferred: String? = nil
let secondary: String? = "Backup"
let unavailable: String? = nil
print(preferred ?? secondary ?? "Offline") // Backup
print(preferred ?? unavailable ?? "Offline") // Offline
The first print chooses Backup because secondary has a value; the second chooses Offline because both optional sources are nil. This is readable when alternatives are simple. If each needs different logging, validation, or recovery behavior, separate branches may explain the decision better than a long expression.
Play with the missing path
You can experiment directly with the examples above, or run them in the Molecule #006 companion. Change savedName from "Mira" to nil and observe that the result becomes "Guest". Then change invalid from "many" to "0": the value produced by ?? 0 remains zero, but the if let branch now succeeds. Each change tests one of the distinctions we just discussed.
Nil coalescing is small because its decision is small. It does not validate a value, store a default, or explain an error. It answers one question: if this optional has a value, use it; otherwise, use that. When that is the decision your code needs to make, a single expression can be clearer than a branch.

