Enums
Swift enums are tagged unions with associated values, computed properties, methods, and protocol conformance. The cornerstone of Swift modeling — Optional, Result, error types — all enums.
Variants, associated, raw, switch, indirect
EXAMPLE
// 1) Basic enum
enum Direction {
case north, east, south, west
}
let d = Direction.north
// 2) Raw values — for serialisation / identifiers
enum Status: String {
case active = "ACTIVE"
case pending = "PENDING"
case archived = "ARCHIVED"
}
let raw = Status.active.rawValue // "ACTIVE"
let status = Status(rawValue: "PENDING") // Status.pending
enum Code: Int {
case ok = 200, notFound = 404, server = 500
}
Code(rawValue: 404) // Code.notFound
// 3) Associated values — each case can carry data
enum NetworkResult<T> {
case success(T)
case failure(Error)
case loading
case offline
}
func handle<T>(_ result: NetworkResult<T>) {
switch result {
case .success(let value):
print("got \\(value)")
case .failure(let error):
print("error: \\(error)")
case .loading:
print("loading…")
case .offline:
print("offline")
}
}
// 4) Switch is EXHAUSTIVE — compiler enforces
func describe(_ d: Direction) -> String {
switch d {
case .north: return "north"
case .east: return "east"
case .south: return "south"
case .west: return "west"
// Adding a case to Direction → all switches must update.
}
}
// 5) Pattern matching with associated values + conditions
func grade(_ score: Int) -> String {
switch score {
case 90...100: return "A"
case 80..<90: return "B"
case let s where s < 0: return "invalid"
default: return "F"
}
}
// 6) Methods + computed properties on enums
enum Shape {
case circle(radius: Double)
case rectangle(width: Double, height: Double)
case triangle(base: Double, height: Double)
var area: Double {
switch self {
case .circle(let r): return .pi * r * r
case .rectangle(let w, let h): return w * h
case .triangle(let b, let h): return 0.5 * b * h
}
}
func scaled(by factor: Double) -> Shape {
switch self {
case .circle(let r): return .circle(radius: r * factor)
case .rectangle(let w, let h): return .rectangle(width: w * factor, height: h * factor)
case .triangle(let b, let h): return .triangle(base: b * factor, height: h * factor)
}
}
}
let area = Shape.circle(radius: 5).area
// 7) Indirect — recursive enums (linked lists, trees, JSON)
indirect enum JsonValue {
case null
case bool(Bool)
case number(Double)
case string(String)
case array([JsonValue])
case object([String: JsonValue])
}
let j: JsonValue = .object([
"name": .string("Ada"),
"age": .number(32),
"tags": .array([.string("engineer"), .string("author")]),
])
// 8) Equatable + Hashable + Codable — auto-synth when all associated values conform
enum HttpMethod: String, Codable {
case get, post, put, delete
}
enum Outcome<T: Codable & Equatable>: Codable, Equatable {
case ok(T)
case error(String)
}
// 9) CaseIterable — iterate every case
enum Day: String, CaseIterable {
case mon, tue, wed, thu, fri, sat, sun
}
for day in Day.allCases {
print(day.rawValue.uppercased())
}
// 10) Discriminated union — events / state machines
enum OrderState {
case cart(items: [Item])
case confirmed(items: [Item], address: Address)
case paid(items: [Item], address: Address, paymentId: String)
case shipped(trackingNo: String)
case cancelled(reason: String)
}
func next(_ state: OrderState, event: Event) -> OrderState {
switch (state, event) {
case (.cart(let items), .confirm(let addr)):
return .confirmed(items: items, address: addr)
case (.confirmed(let items, let addr), .pay(let pid)):
return .paid(items: items, address: addr, paymentId: pid)
case (.paid(_, _, _), .ship(let no)):
return .shipped(trackingNo: no)
default:
return state // invalid transition
}
}
// 11) Optional is an enum — Some(Wrapped) / none
let maybe: Int? = 42
switch maybe {
case .some(let n): print("got \\(n)")
case .none: print("none")
}
// 12) Result is an enum — built into stdlib
let r: Result<Int, Error> = .success(42)
switch r {
case .success(let v): print(v)
case .failure(let e): print(e)
}
// 13) When to use enum vs struct
// enum: finite VARIANTS (each may carry data) — Direction, Status, Event, Result
// struct: composite of fields, all present together — User, Point, Address
// 14) Tips
// • Always handle every case explicitly — avoid `default:` when the set is small + stable
// • Use CaseIterable for menus / pickers / tests
// • Use associated values to model data-bearing variants without separate classes
// • Mark enums @@frozen if you need ABI stability (rare in app code)
Why it matters
Swift enums + exhaustive switch + associated values = type-safe state machines, results, JSON, events. If you find yourself writing a class hierarchy of subclasses, ask if an enum would express it better — usually yes.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
enum Status { case pending, paid, shipped(eta: Date) }
let s: Status = .shipped(eta: .now)
switch s {
case .pending: print("…")
case .paid: print("💸")
case .shipped(let eta): print("by \(eta)")
}
Try it Yourself »
Discussion
Loading…