How it works
The explanation, with examples you can run.
State as an enum
enum LoadState { case idle case loading(progress: Double) case loaded([String]) case failed(message: String, retryable: Bool)} func describe(_ state: LoadState) -> String { switch state { // no default: the compiler checks every case case .idle: "Not started" case .loading(let p): "Loading \(Int(p * 100))%" case .loaded(let items) where items.isEmpty: "Nothing here" case .loaded(let items): "\(items.count) items" case .failed(let message, retryable: true): "\(message), tap to retry" case .failed(let message, _): message }} for s: LoadState in [.idle, .loading(progress: 0.4), .loaded([]), .loaded(["a", "b"]), .failed(message: "Offline", retryable: true)] { print(describe(s))}- The
switchis an expression (Swift 5.9): each branch is a single value and the result is returned directly. whereadds a condition; a guarded case doesn't count as covering everything, so the unguarded.loadedis still needed.- Patterns can match literal values inside a case (
retryable: true) and ignore parts with_.
Compare this to a struct with isLoading: Bool, items: [String]? and error: String?: that allows impossible combinations (loading and failed) that the enum can't represent.
Matching outside switch
let state = LoadState.loaded(["x"])if case .loaded(let items) = state { print(items.first ?? "") } // x let results: [LoadState] = [.failed(message: "A", retryable: false), .idle]for case .failed(let message, _) in results { print(message) } // Aif case, guard case and for case take any pattern. Optional is itself an enum, which is why if case let x? = value works.
More enum features
- Raw values:
enum Weekday: Int { case mon = 1, tue }getsrawValueand a failableinit?(rawValue:);Stringraw values default to the case name. Raw values and associated values can't be mixed. CaseIterable: the compiler writesallCasesfor enums without associated values.- Methods, computed properties and protocol conformances live on enums as on structs.
EquatableandHashableare synthesised when all associated values conform. indirectcases store their payload on the heap, which allows recursive types likeindirect case node(Tree, Tree).
Enums from other modules
Your own enums are frozen as far as the compiler is concerned: it knows every case. Enums from libraries built for library evolution (Apple's SDKs) may gain cases in a future OS. Switching over them asks for @unknown default:, which handles new cases at runtime and still warns at compile time when a known case is missing.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.