Ultimate Guide to Swift Enums: Raw Values, Implicit Assignments, RawValue Initialization & Recursive Enumerations
  • July 24, 2025

1. Enumeration Syntax

An enumeration (or enum) in Swift defines a common type for a group of related values, and enables you to work with those values in a type-safe way.

// Basic enum with no raw values
enum CompassPoint {
    case north
    case south
    case east
    case west
}

// Shorthand: multiple cases on one line
enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
  • The enum keyword introduces the type.
  • Each case defines one of the possible values.
  • Enums in Swift are first-class types: they can have methods, properties, and initializers.

Raw Values (Optional)

You can associate each case with a raw value (of type String, Int, etc.), which lets you convert between the enum and its underlying value.

enum Weekday: Int {
    case monday = 1, tuesday, wednesday, thursday, friday, saturday, sunday
}

enum Direction: String {
    case north = "N", south = "S", east = "E", west = "W"
}

// Implicit raw-value incrementing for integers, and implicit string raw-values matching case names:
enum Fruit: String {
    case apple, banana, cherry
}
// Fruit.banana.rawValue == "banana"
  • You get a failable initializer init?(rawValue:).
  • Use raw values when you need a stable, known mapping (e.g., JSON keys, database IDs).

2. Matching Enumeration Values with a Switch Statement

A switch on an enum is exhaustive, so you must cover all cases (or include a default).

let direction: CompassPoint = .west

switch direction {
case .north:
    print("Heading north")
case .south:
    print("Heading south")
case .east:
    print("Heading east")
case .west:
    print("Heading west")
}

Key points:

  • You can omit the enum’s name in each case when the switch already knows the type (.west instead of CompassPoint.west).
  • Every possible case must be handled. If you add a new case later, the compiler will flag any switch statements that no longer cover all values.
  • You can combine cases:
    switch direction {
    case .north, .south:
        print("Moving vertically")
    case .east, .west:
        print("Moving horizontally")
    }
    

3. Iterating over Enumeration Cases

To loop over all cases of an enum, your type must conform to the CaseIterable protocol:

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

for drink in Beverage.allCases {
    print(drink)
}
// Prints: coffee, tea, juice, water
  • Add : CaseIterable to the enum definition.
  • The compiler synthesizes a static allCases array.
  • You can iterate, count, or randomly pick a case:
    let randomBeverage = Beverage.allCases.randomElement()!
    

4. Associated Values

Associated values let each case carry its own custom data—think of them like lightweight unions or tagged payloads.

enum Barcode {
    case upc(Int, Int, Int, Int)      // tuple of four Ints
    case qrCode(String)               // one String
}

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

Extracting Associated Values

Use a switch to bind and extract:

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let code):
    print("QR code: \(code)")
}
  • You can bind multiple values in a single let:
    case let .upc(numberSystem, manufacturer, product, check):
    
  • Or ignore unwanted values with _:
    case let .upc(_, manufacturer, product, _):
        print("Manufacturer: \(manufacturer), product: \(product)")
    

Associated Values with Named Elements

You can name the elements to improve readability:

enum Measurement {
    case weight(kg: Double)
    case age(years: Int)
}

let m = Measurement.weight(kg: 68.5)
switch m {
case .weight(kg: let w):
    print("Weight: \(w) kg")
case .age(years: let y):
    print("Age: \(y) years")
}

Combining Associated Values and Raw Values

While you can’t mix raw values and associated values on the same enum directly, you can simulate it by using init?(rawValue:) or custom initializers and computed properties.


5. Putting It All Together: A Real-World Example

Imagine you’re parsing messages from a server, where each message can be text, an image with a URL, or a reaction:

enum ChatMessage: CaseIterable {
    case text(String)
    case image(url: URL, thumbnailURL: URL)
    case reaction(emoji: String, toMessageID: Int)
}

// Iterating all possible message "types" (for analytics, UI tabs, etc.):
for type in ChatMessage.allCases {
    print("Supported type: \(type)")
}

// Handling an incoming message:
func handle(_ message: ChatMessage) {
    switch message {
    case .text(let content):
        showText(content)
    case let .image(url, thumbnail):
        showImage(from: url, placeholder: thumbnail)
    case .reaction(let emoji, let messageID):
        addReaction(emoji, to: messageID)
    }
}

This pattern keeps your code:

  • Type-safe: you can’t accidentally forget a case, nor mix up the order of associated data.
  • Self-documenting: the enum’s definition tells you exactly what data accompanies each case.
  • Easy to extend: adding a new message type instantly flags all switch statements needing updates.

6. Cheat Sheet

Feature Syntax / Notes
Declaration enum Name { case a, b, c }
Raw values enum E: Int { case one = 1, two, three } / enum S: String { case foo = "foo" }
Associated values case foo(Int, String) / case bar(name: String, age: Int)
Switch matching switch value { case .foo(let x, let y): … case .bar: … }
Combining cases case .a, .b: …
CaseIterable enum E: CaseIterable { … }E.allCases
Failable raw-value init let e = E(rawValue: 2) returns E?
Implicit init for String raw enum X: String { case a, b }X.a.rawValue == "a"
Ignoring values case .foo(_, let important, _)
Binding multiple case let .foo(x, y, z):

 

YOU MIGHT ALSO LIKE...
🧭 NavigationKit

NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. 💻 Installation 📦 Swift Package Manager Using Swift Package Manager, add ...

swiftui-navigation-stack

An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation. NavigationStack Installation ...

Stinsen

Simple, powerful and elegant implementation of the Coordinator pattern in SwiftUI. Stinsen is written using 100% SwiftUI which makes it ...

SwiftUI Router

With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...

FlowStacks

This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...