Optionals

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

An optional says, in the type itself, whether a value is present. Int? is not "an Int that might secretly be null" the way a reference type is in most other languages — it is a distinct type the compiler forces you to unwrap before using the value inside.

nil and the Optional Enum

Optional<Wrapped> is a genuine two-case enum in the standard library, case none and case some(Wrapped); T? and nil are syntactic sugar over it:

var serverResponseCode: Int? = 404
serverResponseCode = nil                 // now holds "no value" -- legal only because it's an Optional

var surveyAnswer: String?                // no initial value -- automatically nil
print(surveyAnswer == nil)               // true

// What `Int?` desugars to:
let sameThing: Optional<Int> = .some(404)
switch sameThing {
case .none: print("no value")
case .some(let value): print("value is \(value)")
}

Only a type declared with ? (or explicitly Optional<T>) can hold nil — a plain Int or String can never be nil, in contrast to reference types in languages without null safety.

Optional Binding

Optional binding — if let/guard let/while let — tests whether an optional holds a value and, if so, makes that value available as a non-optional constant (or variable) for a scope:

let possibleNumber = "123"

if let actualNumber = Int(possibleNumber) {
    print("\"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
    print("\"\(possibleNumber)\" could not be converted to an integer")
}

// Swift 5.7's shorthand: bind to a new constant of the SAME NAME as the optional.
if let actualNumber {
    print(actualNumber)
}

// Multiple bindings, and a `where` clause, in one `if let`; evaluated left to right,
// each earlier binding available to later clauses -- and short-circuits on the first failure.
func nextSquare(after n: Int?, limit: Int?) -> Int? {
    if let n, let limit, n < limit {
        return n * n
    }
    return nil
}

guard let binds the same way but requires the else branch to exit the current scope (return, throw, break, continue, or fatalError) — the bound value then stays in scope for the rest of the enclosing function, rather than only inside an `if’s braces:

func greet(_ person: [String: String]) -> String {
    guard let name = person["name"] else {
        return "Hi there, stranger!"
    }
    // `name` is available, unwrapped, for the rest of this function -- not just one branch.
    guard let location = person["location"] else {
        return "Hi \(name)!"
    }
    return "Hi \(name)! Glad you could visit from \(location)."
}

while let repeats a loop body for as long as a binding keeps succeeding, stopping the first time it yields nil:

var stack = [1, 2, 3, 4, 5]
while let top = stack.popLast() {
    print(top)      // 5 4 3 2 1
}

The Nil-Coalescing Operator

a ?? b unwraps a if it is non-nil, or evaluates and returns b otherwise; b must have the same type as a’s wrapped value (or be itself optional), and it is evaluated lazily, only when `a is nil:

let defaultColorName = "red"
var userDefinedColorName: String?

var colorNameToUse = userDefinedColorName ?? defaultColorName    // "red"

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName        // "green"

a ?? b is shorthand for a != nil ? a! : b, but without the force-unwrap risk — see Operators for where it sits in the precedence table.

Forced Unwrapping and Implicitly Unwrapped Optionals

The forced-unwrap operator ! accesses an optional’s value directly, trapping (crashing) if it is nil:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

if convertedNumber != nil {
    print("has an integer value of \(convertedNumber!)")   // safe -- checked just above
}

// let crash = Int("not a number")!   // fatal error: unexpectedly found nil while unwrapping

Reach for ! only where you have already proven the value is non-nil in a way the compiler cannot see itself; optional binding is almost always the better default.

An implicitly unwrapped optional, written T!, is declared optional (and can still hold nil) but is automatically force-unwrapped every time it is used as a non-optional T, without needing ! at each call site:

let possibleString: String? = "An optional string."
let forcedString: String = possibleString!            // needs the `!`

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString             // no `!` needed -- unwrapped automatically

if assumedString != nil {                              // still usable as a normal optional
    print(assumedString!)
}
if let definiteString = assumedString {                // and still bindable
    print(definiteString)
}

T! exists almost entirely for values that are set once, immediately after initialization, and never nil again in practice — a @IBOutlet, or a two-phase-initialized property — where writing T? everywhere would force unwraps at every use site for a value that is, in practice, always present. Prefer a genuine T? when there is any real chance the value is absent.

Optional Chaining

Optional chaining — ?./?[…​] after an optional — calls a property, method, or subscript only if the optional is non-nil, and the whole expression evaluates to nil immediately if any link in the chain is nil, without crashing:

class Residence {
    var numberOfRooms = 1
    var address: Address?
}
class Person {
    var residence: Residence?
}
class Address {
    var buildingName: String?
    func printAddress() { print("some address") }
}

let john = Person()

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")   // printed -- john.residence is nil
}

john.residence?.address?.printAddress()                // no-op, no crash: the whole chain short-circuits
let count = john.residence?.address?.buildingName?.count   // Int?, chained through three optionals

Every step in a chain, and the whole expression’s result, is optional — even a property that is itself Int becomes Int? once accessed through ?., because any link along the way might have been nil. Calling a method through a chain that returns Void yields Void?, useful for testing whether the call happened at all: if (someOptional?.doSomething()) != nil { …​ }.

The Optional Pattern, map/flatMap, and Comparing Optionals

The optional pattern matches only the .some case, binding its payload, and reads as the value’s type followed by ?:

let someOptional: Int? = 42

if case let x? = someOptional {          // equivalent to `if case .some(let x) = someOptional`
    print(x)
}

for case let number? in [1, nil, 3, nil, 5] {   // skips every nil element
    print(number)                                // 1 3 5
}

map transforms the wrapped value in place, staying optional; flatMap does the same but flattens away a result that would otherwise be a nested optional:

let possibleNumber: Int? = 4
let possibleSquare = possibleNumber.map { $0 * $0 }          // Optional(16)

func rootIfPositive(_ n: Int) -> Int? { n >= 0 ? Int(Double(n).squareRoot()) : nil }
let nested = possibleNumber.map(rootIfPositive)              // Int?? -- an optional optional
let flattened = possibleNumber.flatMap(rootIfPositive)       // Int? -- flattened back down

Optionals conform to Equatable/Comparable whenever their Wrapped type does, and nil compares equal only to nil and always sorts before every wrapped value:

print(Int?.none == Int?.none)     // true
print(Optional(1) == Optional(1)) // true
print(Optional(1) == nil)         // false
print([3, nil, 1, nil, 2].sorted { ($0 ?? Int.min) < ($1 ?? Int.min) })   // a common trick for sorting with nils first
flowchart TD A["You have T? and need the T"] --> B{"Is nil expected and\nrecoverable right here?"} B -->|"yes, with a fallback value"| C["a ?? defaultValue"] B -->|"yes, continue only if present"| D{"Inside a function,\nrest of scope needs it?"} D -->|"yes"| E["guard let x = a else { return }"] D -->|"no, just this block"| F["if let x = a { ... }"] A --> G{"Chaining through\nmultiple optionals?"} G -->|"yes"| H["a?.b?.c"] A --> I{"Absolutely certain\nit's non-nil here?"} I -->|"yes, proven earlier"| J["a! (traps if wrong)"] I -->|"no"| K["Don't force-unwrap --\nuse binding or ??"] style C fill:#eefaf2,stroke:#2f7d51 style E fill:#eefaf2,stroke:#2f7d51 style F fill:#eefaf2,stroke:#2f7d51 style H fill:#eefaf2,stroke:#2f7d51 style J fill:#fff4e8,stroke:#b5762a style K fill:#fdf3f3,stroke:#b5523d

See Also