Enumerations

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

An enumeration defines a common type for a group of related values and lets you work with those values in a type-safe way throughout your code. Unlike enumerations in C, a Swift enum case is not backed by an integer by default — each case is a first-class value in its own right, and cases may carry their own associated data.

Enum Syntax and Matching with switch

enum CompassPoint {
    case north
    case south
    case east
    case west
}

var directionToHead = CompassPoint.west
directionToHead = .west                          // the type is already known, so `.west` alone is enough

switch directionToHead {
case .north:
    print("Lots of planets have a north")
case .south:
    print("Watch out for penguins")
case .east:
    print("Where the sun rises")
case .west:
    print("Where the skies are blue")
}                                                 // exhaustive: every CompassPoint case is handled, no default needed

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune   // multiple cases on one line
}

A switch over an enum must be exhaustive: every case declared on the type has to be handled, either directly or via default, which is what lets the compiler catch a forgotten case the moment a new one is added (see Pattern Matching for the enumeration-case pattern this relies on, and Control Flow for switch as a statement and an expression).

CaseIterable

enum Beverage: CaseIterable {
    case coffee, tea, juice
}

let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")   // 3

for beverage in Beverage.allCases {
    print(beverage)
}

Conforming an enum to CaseIterable synthesizes an allCases static property listing every case in declaration order, useful for populating a menu, iterating exhaustively in a test, or counting the cases without maintaining a separate list by hand.

Associated Values

enum Barcode {
    case upc(Int, Int, Int, Int)         // unlabeled associated values
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let productCode):
    print("QR code: \(productCode)")
}

switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):     // a single `let` before the pattern binds every element
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case let .qrCode(productCode):
    print("QR code: \(productCode)")
}

enum HTTPResponse {
    case ok(body: String)                // labeled associated values document what each payload means
    case redirect(location: String, statusCode: Int)
    case error(message: String)
}

let response = HTTPResponse.redirect(location: "/login", statusCode: 302)
if case .redirect(let location, let statusCode) = response {
    print("redirecting to \(location) with status \(statusCode)")
}

Associated values let each case carry data of its own choosing, extracted through the same pattern-matching machinery as any other enumeration-case pattern — a switch, if case, or guard case. Labels on associated values (body:, location:, statusCode:) are purely documentation at the declaration site; extracting them still binds by position, matching the label names used when constructing the case.

Raw Values

enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

enum Planet2: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune   // implicit assignment: 1, 2, 3...
}
print(Planet2.mars.rawValue)                     // 4

enum CompassPoint2: String {
    case north, south, east, west                 // implicit assignment: the case name itself, as a String
}
print(CompassPoint2.south.rawValue)              // "south"

let possiblePlanet = Planet2(rawValue: 7)        // init?(rawValue:) -- always failable
if let possiblePlanet {
    print(possiblePlanet)
} else {
    print("no such planet")
}
let notAPlanet = Planet2(rawValue: 11)           // nil: no case has raw value 11

A raw-value enum stores one pre-populated value per case, of a single fixed type (Character, String, or any integer or floating-point type). Integer raw values auto-increment from the first explicit one (or from 0); string raw values default to the case’s own name when omitted. The compiler-synthesized init?(rawValue:) is always failable, since not every raw value maps to a case — unlike associated values, which are supplied at construction and read back by pattern matching rather than through a raw-value initializer.

Recursive Enums with indirect

indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

// enum ArithmeticExpression2 {
//     case number(Int)
//     indirect case addition(ArithmeticExpression2, ArithmeticExpression2)   // `indirect` on just this case also works
//     case multiplication(ArithmeticExpression2, ArithmeticExpression2)
// }

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case .number(let value):
        value
    case .addition(let left, let right):
        evaluate(left) + evaluate(right)
    case .multiplication(let left, let right):
        evaluate(left) * evaluate(right)
    }
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
print(evaluate(product))                          // 18

An enum case whose associated value is the enum’s own type would otherwise have unbounded size at compile time; marking the case (or the whole enum) indirect tells the compiler to box that case’s storage on the heap so the recursion has a fixed, indirect size, at the cost of an extra allocation and pointer indirection for that case.

Methods, Computed Properties and Initializers on Enums

enum TrafficLight {
    case red, yellow, green

    var next: TrafficLight {                       // computed property
        switch self {
        case .red: .green
        case .green: .yellow
        case .yellow: .red
        }
    }

    func description() -> String {                 // instance method
        switch self {
        case .red: "stop"
        case .yellow: "caution"
        case .green: "go"
        }
    }

    init(fromSeconds seconds: Int) {                // custom initializer
        self = seconds.isMultiple(of: 2) ? .red : .green
    }
}

var light = TrafficLight.red
light = light.next                                  // .green
print(light.description())

Like structs and classes, an enum can declare instance methods, computed properties, and its own initializers — covered in full in Properties and Methods and Subscripts. It cannot, however, declare stored properties per case beyond the case’s own associated values.

Enums as Namespaces

enum MathConstants {                                // never instantiated -- purely a namespace
    static let pi = 3.14159
    static func square(_ x: Double) -> Double { x * x }
}

print(MathConstants.pi)
print(MathConstants.square(4))

An enum with no cases at all can never be instantiated, which makes it a convenient namespace for grouping related static constants and functions without accidentally creating a value of the type — a lighter-weight alternative to a struct used the same way.

OptionSet Structs vs. Enums

struct ShippingOptions: OptionSet {
    let rawValue: Int

    static let nextDay    = ShippingOptions(rawValue: 1 << 0)
    static let secondDay  = ShippingOptions(rawValue: 1 << 1)
    static let priority   = ShippingOptions(rawValue: 1 << 2)
    static let standard   = ShippingOptions(rawValue: 1 << 3)

    static let express: ShippingOptions = [.nextDay, .secondDay]
}

let options: ShippingOptions = [.priority, .standard]
if options.contains(.standard) {
    print("standard shipping included")
}

OptionSet — always a struct, never an enum — models a combinable set of flags (any subset of options at once, via [.a, .b]), which an enum’s mutually-exclusive cases cannot express directly; reach for an enum when exactly one case applies at a time, and for OptionSet when several may apply together.

Comparable Synthesis

enum SkillLevel: Comparable {
    case beginner
    case intermediate
    case expert
}

let levels: [SkillLevel] = [.expert, .beginner, .intermediate]
print(levels.sorted())                              // [.beginner, .intermediate, .expert]
print(SkillLevel.beginner < SkillLevel.expert)      // true

Conforming a raw-value-less enum to Comparable synthesizes < from declaration order, so cases compare in the order they were written — convenient for a small, ordered set of levels or states without hand-writing the comparison.

Enums as State Machines

stateDiagram-v2 [*] --> idle idle --> loading: start() loading --> loaded: succeed(data:) loading --> failed: fail(error:) failed --> loading: retry() loaded --> [*]
enum DownloadState {
    case idle
    case loading
    case loaded(data: Data)
    case failed(error: Error)

    mutating func start() {
        guard case .idle = self else { return }
        self = .loading
    }

    mutating func succeed(data: Data) {
        guard case .loading = self else { return }
        self = .loaded(data: data)
    }

    mutating func fail(error: Error) {
        guard case .loading = self else { return }
        self = .failed(error: error)
    }
}

An enum with one case per state, each carrying exactly the data that state needs (a loaded case’s data, a failed case’s error), models a state machine directly: the compiler enforces that every transition method starts from a known set of cases, and a switch over the state can never omit a case it hasn’t been taught about. mutating methods that reassign self — as start(), succeed(data:) and fail(error:) do above — are covered in full in Methods and Subscripts.

See Also

  • Pattern Matching — the enumeration-case pattern used throughout this page’s switch/if case examples.

  • Structures and Classes — enums as one of Swift’s two value-type kinds, alongside structs.

  • Properties — computed properties and property observers in full, applicable to enum cases too.

  • Methods and Subscripts — mutating methods on value types, the mechanism behind this page’s state-machine example.