Skip to content

Enumerations

Enumerations (enum) define a finite set of related values, supporting raw values, associated values, and recursive definitions. They are powerful for modeling state and data variants.

Basic Enumerations

Define with enum, where cases represent possible values.

Example:

swift
enum Direction {
    case north
    case south
    case east
    case west
}

let heading = Direction.north
switch heading {
case .north: print("Going north")
default: print("Other direction")
}

Raw Values

Assign raw values (e.g., Int, String) to cases, automatically incrementing for integers.

Example: String Raw Values:

swift
enum Planet: String {
    case mercury = "Mercury"
    case venus = "Venus"
    case earth = "Earth"
}

let planet = Planet(rawValue: "Earth") // .earth
print(planet?.rawValue ?? "Unknown") // "Earth"

Example: Integer Raw Values:

swift
enum StatusCode: Int {
    case ok = 200
    case notFound = 404
    case serverError = 500
}

let code = StatusCode(rawValue: 404) // .notFound
print(code?.rawValue) // 404

Associated Values

Store additional data with cases, enabling flexible data modeling.

Example:

swift
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

let barcode = Barcode.upc(123, 456, 789, 0)
switch barcode {
case let .upc(a, b, c, d):
    print("UPC: \(a)-\(b)-\(c)-\(d)")
case let .qrCode(content):
    print("QR: \(content)")
}

Recursive Enumerations

Use indirect for cases that reference the enum itself.

Example:

swift
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
    
    func evaluate() -> Int {
        switch self {
        case let .number(value):
            return value
        case let .addition(left, right):
            return left.evaluate() + right.evaluate()
        case let .multiplication(left, right):
            return left.evaluate() * right.evaluate()
        }
    }
}

let expr = ArithmeticExpression.addition(.number(5), .multiplication(.number(2), .number(3)))
print(expr.evaluate()) // 5 + (2 * 3) = 11

Methods and Properties

Enums can have methods, computed properties, and static members.

Example:

swift
enum TrafficLight {
    case red, yellow, green
    
    var duration: Int {
        switch self {
        case .red: return 30
        case .yellow: return 5
        case .green: return 20
        }
    }
    
    func next() -> TrafficLight {
        switch self {
        case .red: return .green
        case .green: return .yellow
        case .yellow: return .red
        }
    }
    
    static func defaultLight() -> TrafficLight {
        return .red
    }
}

var light = TrafficLight.red
print(light.duration) // 30
light = light.next() // .green
print(TrafficLight.defaultLight()) // .red

CaseIterable Protocol

Generate a collection of all cases with CaseIterable.

Example:

swift
enum Day: CaseIterable {
    case monday, tuesday, wednesday, thursday, friday, saturday, sunday
}

print(Day.allCases.count) // 7
for day in Day.allCases {
    print(day)
}

Frozen Enums

Use @frozen (in libraries) to guarantee no new cases are added, improving performance.

Example:

swift
@frozen enum LogLevel {
    case debug
    case info
    case error
}

Enums with Protocols

Enums can conform to protocols.

Example:

swift
protocol Describable {
    var description: String { get }
}

enum FileType: String, Describable {
    case pdf, doc, txt
    
    var description: String {
        return "File type: \(rawValue.uppercased())"
    }
}

let file = FileType.pdf
print(file.description) // "File type: PDF"

Best Practices

  • Use Enums for States: Model finite states (e.g., UI modes, API responses).
  • Associated Values: Store variant-specific data.
  • Raw Values: Use for serialization or external representation.
  • CaseIterable: Iterate over cases when needed.
  • Methods: Add logic to enums for self-contained behavior.
  • Frozen Enums: Use in libraries for performance and stability.
  • Exhaustive Switches: Avoid default for enums to catch future changes.

Troubleshooting

  • Non-Exhaustive Switch: Add all cases or default.
  • Raw Value Failure: Verify input matches raw value type.
  • Recursive Enum Crash: Ensure indirect is used correctly.
  • Protocol Conformance: Check required members are implemented.
  • Performance: Avoid complex logic in enum methods for critical paths.

Example: Comprehensive Enum Usage

swift
enum NetworkRequest: CaseIterable, Describable {
    case get(url: String)
    case post(url: String, data: [String: Any])
    case delete(id: Int)
    
    var description: String {
        switch self {
        case let .get(url):
            return "GET request to \(url)"
        case let .post(url, data):
            return "POST request to \(url) with \(data)"
        case let .delete(id):
            return "DELETE request for ID \(id)"
        }
    }
    
    var method: String {
        switch self {
        case .get: return "GET"
        case .post: return "POST"
        case .delete: return "DELETE"
        }
    }
    
    static var allMethods: [String] {
        allCases.map { $0.method }
    }
}

let requests: [NetworkRequest] = [
    .get(url: "https://api.example.com/users"),
    .post(url: "https://api.example.com/users", data: ["name": "Alice"]),
    .delete(id: 123)
]

for request in requests {
    print(request.description)
}
print(NetworkRequest.allMethods) // ["GET", "POST", "DELETE"]

Released under the MIT License.