iosNudge

What are optionals in Swift, and how do you unwrap them safely?

EasyAsked oftenSwift 2 min read

TL;DR

An optional is a type that can hold a value or nil. You write it as String?. You must unwrap it before you use the value. Safe ways: if let, guard let, ??, optional chaining ?., and map. Avoid ! unless a nil is truly a programmer error.

An optional is just an enum

enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}

let name: String? = "Ana"   // same as .some("Ana")
let none: String? = nil     // same as .none

Knowing this helps in interviews. It explains why you can switch over an optional.

switch name {
case .some(let value): print("Hi \(value)")
case .none: print("No name")
}

if let — use the value only inside a block

let input: String? = "42"

if let input, let number = Int(input) {
    print(number + 1)       // 43
} else {
    print("Not a number")
}

Since Swift 5.7 you can write if let input instead of if let input = input.

guard let — exit early, keep the value after

func greet(_ user: User?) {
    guard let user else {
        print("No user")
        return
    }
    print("Hello \(user.name)")   // user is non-optional here
}

Use guard when nil means "stop". It keeps the happy path flat, not nested.

?? — give a default

let title: String? = nil
let label = title ?? "Untitled"   // "Untitled"

Optional chaining ?.

Call a property or method only if the value exists. The result is always optional.

let count = user?.address?.city.count   // Int?

If any link is nil, the whole chain is nil. No crash.

map and flatMap

let text: String? = "5"
let doubled = text.flatMap { Int($0) }.map { $0 * 2 }   // Optional(10)
  • map: transform the value if it exists.
  • flatMap: transform with a function that itself returns an optional. Avoids Int??.

Force unwrap ! — use with care

let url = URL(string: "https://example.com")!

This crashes if the value is nil. It is fine for a hard-coded constant you know is valid. Do not use it on user input or network data.

Implicitly unwrapped optionals T!

@IBOutlet weak var titleLabel: UILabel!

It is still an optional, but Swift unwraps it for you on use. If it is nil, the app crashes. You mostly see it with @IBOutlet, because the outlet is set after init.

Comparison

ToolWhen to use
if letDo something only when a value exists
guard letNeed the value for the rest of the function
??A sensible default exists
?.Reach into nested optionals
!nil would be a bug, and you want a crash

Common mistakes

  • Force unwrapping data from JSON or user input.
  • Deep nesting of if let. Use guard or combine them with commas.
  • Comparing to nil and then force unwrapping. Use if let instead.
// Bad
if user != nil { print(user!.name) }
// Good
if let user { print(user.name) }

Common follow-up questions

  • What is the difference between nil in Swift and Objective-C? In Objective-C, nil is only for objects (a null pointer). In Swift, any type can be optional, including Int and structs.
  • What does try? return? An optional. It is nil if the function throws.
  • What is a double optional? Int??. You get it from things like dictionary[key] when the value type is itself optional. Use flatMap or pattern matching to flatten it.
  • Can you use optionals in a for loop? Yes, with for case let x? in array to skip nil values. Or use compactMap.
  • Why does Swift have optionals at all? To make "no value" explicit in the type system, so the compiler forces you to handle it.