iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…