Swift Optionals

Optionals are Swift's solution to the null reference problem. They make you handle missing values explicitly, preventing crashes.

What Are Optionals?

SWIFT
// Non-optional: always has a value
let name: String = "Alice"

// Optional: might have a value, might be nil
var maybeName: String? = "Alice"
maybeName = nil  // OK

// The ? means "String or nil"
// The type is Optional<String>, not String

Unwrapping Optionals

SWIFT
// 1. if let — Conditional unwrapping
if let name = maybeName {
    print("Name is \(name)")
} else {
    print("Name is nil")
}

// 2. guard let — Early exit (preferred in functions)
func processUser(name: String?) {
    guard let name = name else {
        print("Error: name required")
        return
    }
    // name is now a non-optional String
    print("Processing \(name)")
}

// 3. Nil coalescing (??) — Default value
let displayName = maybeName ?? "Anonymous"

// 4. Optional chaining — Safe property/method access
let upperName = maybeName?.uppercased()  // "ALICE" or nil
let length = maybeName?.count            // 5 or nil

// Chained optional access
struct Address { var city: String }
struct Person { var address: Address? }

let person: Person? = Person(address: Address(city: "NYC"))
let city = person?.address?.city  // "NYC"

// 5. Force unwrap (!) — DANGEROUS, crashes if nil
// let forcedName = maybeName!  // Crashes if nil!

Optional Patterns

SWIFT
// map — Transform optional value
let maybeAge: Int? = 28
let ageMessage = maybeAge.map { "Age: \($0)" }  // Optional("Age: 28")

// flatMap — Transform that might produce nil
let maybeString: String? = "42"
let maybeInt = maybeString?.flatMap { Int($0) }  // Optional(42)

// compactMap — Filter nil from arrays
let strings = ["1", "2", "three", "4"]
let numbers = strings.compactMap { Int($0) }  // [1, 2, 4]

// allSatisfy with optionals
let names: [String?] = ["Alice", nil, "Bob"]
let validNames = names.compactMap { $0 }  // ["Alice", "Bob"]

// Optional in switch
switch maybeName {
case .some(let name):
    print("Name: \(name)")
case .none:
    print("No name")
}

// Or simpler:
switch maybeName {
case let name?:
    print("Name: \(name)")
case nil:
    print("No name")
}

Working with Optionals

SWIFT
// Collection of optionals
let scores: [Int?] = [95, nil, 87, nil, 92]

// Filter out nils
let validScores = scores.compactMap { $0 }  // [95, 87, 92]
let total = validScores.reduce(0, +)         // 274

// Optional in dictionaries
let userDict: [String: String?] = [
    "name": "Alice",
    "email": nil,
    "phone": "555-1234"
]

// Safe access
if let email = userDict["email"] {
    print(email ?? "No email")
} else {
    print("Key not found")
}

// Optional as function return
func findUser(id: Int) -> String? {
    let users = ["Alice", "Bob", "Charlie"]
    guard id >= 0 && id < users.count else { return nil }
    return users[id]
}

let user = findUser(id: 1)  // Optional("Bob")
let noUser = findUser(id: 99)  // nil

// Handling multiple optionals
func process(_ a: Int?, _ b: Int?) -> Int? {
    guard let a = a, let b = b else { return nil }
    return a + b
}

process(3, 5)   // Optional(8)
process(3, nil) // nil

Common Patterns

SWIFT
// Result type (success or failure)
enum NetworkError: Error {
    case timeout
    case invalidResponse
    case unauthorized
}

func fetchData() -> Result<String, NetworkError> {
    // ... implementation
    return .success("data")
}

switch fetchData() {
case .success(let data):
    print("Got: \(data)")
case .failure(let error):
    print("Error: \(error)")
}

// Optional in enums
enum HTTPMethod {
    case get
    case post(body: Data?)
}

let method = HTTPMethod.post(body: nil)
if case .post(let body) = method {
    print(body ?? "No body")
}

💡 Tip: Prefer guard let over if let when you need to unwrap a value at the start of a function. It reduces nesting and makes your happy path clearer.

Next: Swift Structs and Closures