# Swift Enumerations Explained: Cases, Raw Values, and Associated Values

## Pro Summary

Swift enums are value-type sum types: a value is exactly one case, optionally carrying associated values, and the compiler enforces exhaustive `switch`. Enums without associated values are automatically `Equatable` and `Hashable`; with payloads you must declare those conformances (synthesized when every payload conforms), and `Codable` is synthesized for payload cases since Swift 5.5. When switching over enums you don't own (SDK or resilient-library enums), add `@unknown default` so newly added cases surface as diagnostics instead of disappearing into a plain `default`.

```
enum Code: Int { case ok = 200, created }   // created == 201

enum Payload: Hashable, Codable {
    case text(String)
    case pair(x: Int, y: Int)
    case empty
    indirect case wrapped(Payload)
}
```

Requires: Any Swift version. Synthesized `Codable` for cases with associated values: Swift 5.5+ (SE-0295). Omitting `@unknown default` when switching over a non-frozen SDK/C enum is an error in Swift 6 language mode (a warning in Swift 5 mode).

Gotchas:

- If your enum has a case named `none`, `value == .none` on a `MyEnum?` resolves to `Optional.none` (the compiler only warns), so the check is false for `MyEnum.none`; write `== MyEnum.none` or `if case .some(.none) = value`.
- Implicit raw values are positional: `String` raw values default to the case name, and `Int` raw values count up from the previous explicit value (`case ok = 200, created` gives `created == 201`), so inserting or reordering cases silently changes persisted values unless every raw value is explicit.
- `CaseIterable` is synthesized only when no case has associated values; declaring it on a payload enum fails to compile, so you have to write `allCases` yourself.
- Synthesized `Codable` keys each value by case name and encodes unlabeled payloads as `_0`, `_1` (`{"rect":{"_0":1,"_1":2}}`), so renaming a case or adding argument labels breaks previously stored data.
- Adding a case to a public enum breaks every client's exhaustive `switch`; for non-`@frozen` enums in library-evolution builds (and imported C enums), clients are expected to handle `@unknown default`.

Primary sources:

- [The Swift Programming Language: Enumerations](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/)
- [SE-0295: Codable synthesis for enums with associated values](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0295-codable-synthesis-for-enums-with-associated-values.md)
- [SE-0192: Handling Future Enum Cases](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0192-non-exhaustive-enums.md)
- [SE-0194: Derived Collection of Enum Cases (CaseIterable)](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0194-derived-collection-of-enum-cases.md)

Source: https://devdocs10.com/articles/swift/swift-enumerations  
By Murat Bo · Updated 2026-10-17 · DevDocs10 (https://devdocs10.com)

Swift enumerations, usually written as `enum`, let you define a fixed set of related values under one type. They are one of Swift&rsquo;s most useful custom types because they make code clearer, safer, and easier to reason about than passing around loosely related strings, numbers, or Boolean combinations.

> **Quick answer:** A Swift enumeration is a custom type that groups a known set of possible cases, such as directions, network states, or user roles. Enums help you model data more safely, and Swift can often check at compile time that you handled every possible case.

> **Difficulty:** Beginner
>
> **Helpful to know first:** basic Swift syntax, how constants and variables work, and simple types like `String`, `Int`, and functions.

## 1. What Is Enumerations?

An enumeration is a custom Swift type that represents a small, defined set of possible values. Each possible value is called a *case*. Instead of using random strings like `"success"` or numbers like `0` and `1`, you can create a meaningful type with named cases.

For example, a traffic light has a limited set of states. That makes it a good fit for an enum.

- An enum defines a fixed list of allowed cases.
- Each case belongs to the enum&rsquo;s type.
- Enums improve readability because case names describe intent.
- Enums improve safety because invalid values are harder to create.
- Swift enums are powerful: they can also store raw values, associated values, properties, methods, and conform to protocols.

Beginners often compare enums to strings or integers. The key difference is that strings and integers can represent many unrelated values, while an enum only allows the cases you defined. That makes bugs easier to prevent.

Here is a simple enum declaration:

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

This code creates a new type named `Direction`. A value of that type can only be one of four cases.

> In Swift, enum cases are values in their own right. They are not just labels attached to numbers like they often are in some other languages.

## 2. Why Enumerations Matters

Enums matter because many programming problems involve a value that must be one of a small number of known options. When you model those options as an enum, your code becomes more explicit and more reliable.

Here are common reasons to use enums:

- To represent status values such as `loading`, `success`, and `failure`.
- To model categories like user roles, directions, payment methods, or file types.
- To make `switch` statements safer, because Swift can require exhaustive handling.
- To avoid stringly typed code, where spelling mistakes can create bugs.
- To attach related data using associated values.

Imagine you store order status as a string:

```swift
let status = "shippd"
```

This compiles, but the value is wrong because of a typo. With an enum, that kind of mistake is much harder to make.

```swift
enum OrderStatus {
    case pending
    case processing
    case shipped
    case delivered
}
```

Now your program works with a dedicated type instead of arbitrary text.

Enums are especially valuable in Swift because the language uses them heavily in its own design. `Optional` is an enum behind the scenes, and many APIs use enums to model states and choices safely.

## 3. Basic Syntax

The basic syntax starts with the `enum` keyword, followed by a type name and one or more `case` declarations.

### Declaring an enum

This is the smallest useful pattern:

```swift
enum Season {
    case spring
    case summer
    case autumn
    case winter
}
```

`Season` is the enum type, and the four case names are the only valid values of that type.

### Creating enum values

You can assign a case to a variable or constant using dot syntax:

```swift
let favoriteSeason: Season = .autumn
```

Because the type is already known on the left side, Swift lets you write `.autumn` instead of `Season.autumn`.

You can also write the full form:

```swift
let currentSeason = Season.summer
```

Both forms create a value of type `Season`.

### Multiple cases on one line

Swift also allows multiple cases in one declaration:

```swift
enum CompassPoint {
    case north, south, east, west
}
```

This is equivalent to writing each case on its own line.

### Using enums with switch

One of the most important ideas in Swift enums is that they work naturally with `switch`. Swift checks that all cases are handled unless you use a default branch.

```swift
let direction = Direction.east

switch direction {
case .north:
    print("Going up")
case .south:
    print("Going down")
case .east:
    print("Going right")
case .west:
    print("Going left")
}
```

Output (tested with Swift 6.4):

```text
Going right
```

This shows why enums are so useful: the language knows exactly which cases exist.

> A common beginner question is whether enums are like structs or classes. An enum is its own custom type category in Swift. Use it when a value should be one of several known cases, not when you need many stored properties like a typical struct or reference semantics like a class.

## 4. Step-by-Step Examples

The examples below build from simple cases to more powerful enum features you will use in real Swift code.

### Example 1: Representing a simple choice

This example uses an enum to store a player difficulty setting. It is clearer than using strings like `"easy"` or `"hard"`.

```swift
enum Difficulty {
    case easy
    case medium
    case hard
}

let selectedDifficulty = Difficulty.medium
print(selectedDifficulty)
```

Output (tested with Swift 6.4):

```text
medium
```

This creates a value that can only be one of the three defined difficulty levels.

### Example 2: Using a switch with an enum

Here, the enum controls program behavior. This is one of the most common enum patterns in Swift.

```swift
enum ConnectionState {
    case disconnected
    case connecting
    case connected
}

let state = ConnectionState.connecting

switch state {
case .disconnected:
    print("No network connection")
case .connecting:
    print("Connecting...")
case .connected:
    print("Connected successfully")
}
```

Output (tested with Swift 6.4):

```text
Connecting...
```

The enum gives the switch a controlled set of possibilities, and Swift checks that none are forgotten.

### Example 3: Enum with raw values

Enums can assign each case a raw value. A raw value is a built-in value such as a string or integer that is associated with each case.

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

let status = HTTPStatus.notFound
print(status.rawValue)
```

Output (tested with Swift 6.4):

```text
404
```

This prints `404`. Raw values are useful when your enum cases map to external data, database values, or protocol codes.

You can also create an enum from a matching raw value:

```swift
let responseCode = 200
let httpStatus = HTTPStatus(rawValue: responseCode)

print(httpStatus as Any)
```

This initializer returns an optional because the integer might not match any case.

### Example 4: Enum with associated values

Associated values are different from raw values. Raw values are fixed and shared by a case definition. Associated values store extra data for each specific enum value.

```swift
enum LoginResult {
    case success(String)
    case failure(String)
}

let result = LoginResult.failure("Wrong password")

switch result {
case .success(let username):
    print("Welcome, \(username)!")
case .failure(let message):
    print("Login failed: \(message)")
}
```

Output (tested with Swift 6.4):

```text
Login failed: Wrong password
```

This is a powerful pattern because the enum does not just say what happened. It can also carry the related data for that case.

> Raw values and associated values are often confused. A raw value is predefined for every case, like `404` for `notFound`. An associated value is extra data stored with an individual enum value at runtime, like a specific error message.

## 5. Practical Use Cases

Enums are most useful when your data naturally falls into a small set of well-defined options. Here are practical situations where they improve Swift code:

- App state management, such as `idle`, `loading`, `loaded`, and `failed`.
- Network result types, especially when success and failure need different associated data.
- User roles like `guest`, `member`, and `admin`.
- Input modes such as card payment, bank transfer, or cash on delivery.
- Direction, navigation, or movement logic in games and command-line tools.
- Parsing external values such as HTTP status codes, file formats, or configuration options.
- Feature flags where only certain named modes are valid.

As a rule, use an enum when a value should be one of several known choices. If you instead need a type that stores many independent pieces of data together, a `struct` is often the better fit.

For example, this is a strong enum use case:

```swift
enum PaymentMethod {
    case creditCard
    case bankTransfer
    case cash
}
```

But this kind of data usually belongs in a struct instead:

```swift
struct Customer {
    let name: String
    let email: String
    let age: Int
}
```

The enum models one choice from a limited set. The struct models an object with multiple stored properties.

## 6. Common Mistakes

Swift enums are very readable once you understand them, but beginners often run into the same few problems. Most of these issues come from forgetting that enum values must match a defined case exactly, or from not handling every possible case in a `switch`.

### Mistake 1: Using a case that does not exist

An enum can only use the cases declared inside its definition. You cannot invent new values later.

**Problem:** This code tries to assign a case that was never declared in the enum, so Swift reports that the member does not exist.

```swift
enum TrafficLight {
    case red
    case yellow
    case green
}

let light = TrafficLight.blue
```

**Fix:** Use only one of the declared cases, or add the missing case to the enum if it is a real valid state.

```swift
enum TrafficLight {
    case red
    case yellow
    case green
}

let light = TrafficLight.green
```

The corrected version works because `green` is a real case defined by the enum.

### Mistake 2: Forgetting that switch must be exhaustive

When you switch over an enum, Swift usually requires you to handle every possible case. This makes your code safer because no state is silently ignored.

**Problem:** This switch does not cover all enum cases, so Swift shows an error such as `Switch must be exhaustive`.

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

let direction = Direction.east

switch direction {
case .north:
    print("Up")
case .south:
    print("Down")
}
```

**Fix:** Cover every case explicitly, or use `default` when that is truly appropriate.

```swift
switch direction {
case .north:
    print("Up")
case .south:
    print("Down")
case .east:
    print("Right")
case .west:
    print("Left")
}
```

The corrected version works because every possible enum value is handled.

### Mistake 3: Confusing raw values with associated values

Raw values are fixed values attached to each case definition. Associated values are extra data stored with a specific enum instance.

**Problem:** This code tries to pass data into a raw-value enum case, but raw values do not work that way.

```swift
enum Status: String {
    case success = "success"
    case error = "error"
}

let result = Status.error("Network failed")
```

**Fix:** Use an enum with associated values when each case may carry extra information.

```swift
enum Status {
    case success
    case error(String)
}

let result = Status.error("Network failed")
```

The corrected version works because the `error` case is defined to store a `String` message.

### Mistake 4: Treating raw values as automatic case lookups

If an enum has raw values, creating an enum from a raw value returns an optional. The raw value might not match any case.

**Problem:** This code assumes Swift will always find a matching case, but the initializer can return `nil`.

```swift
enum Role: String {
    case admin
    case editor
    case viewer
}

let role: Role = Role(rawValue: "owner")
```

**Fix:** Unwrap the optional result safely with `if let` or provide fallback logic.

```swift
if let role = Role(rawValue: "owner") {
    print(role)
} else {
    print("Unknown role")
}
```

The corrected version works because it handles the possibility that no matching enum case exists.

## 7. Best Practices

Enums become especially powerful when you use them to model clear, intentional states. These practices help keep your enums easy to read and easy to maintain.

### Practice 1: Use enums for closed sets of valid choices

If a value should come from a fixed list, an enum is usually safer than a plain string. Strings are easy to mistype and harder to validate.

A less-preferred approach uses strings directly:

```swift
let userRole = "admim"
```

A better approach uses an enum:

```swift
enum UserRole {
    case admin
    case editor
    case viewer
}

let userRole = UserRole.admin
```

This is better because Swift now prevents invalid role values at compile time.

### Practice 2: Prefer meaningful associated values over separate loose variables

When a state needs extra data, store that data inside the enum case instead of spreading it across multiple variables.

A less-preferred approach keeps related state disconnected:

```swift
let isError = true
let errorMessage = "File not found"
```

A better approach groups the state and its data together:

```swift
enum FileResult {
    case success(String)
    case failure(String)
}

let result = FileResult.failure("File not found")
```

This is better because the data is attached directly to the state it belongs to.

### Practice 3: Keep switch statements explicit when clarity matters

Although `default` can be convenient, explicitly handling each case is often clearer, especially when the enum has only a few cases.

A less-preferred approach hides behavior inside a catch-all branch:

```swift
switch userRole {
case .admin:
    print("Full access")
default:
    print("Limited access")
}
```

A better approach spells out the cases:

```swift
switch userRole {
case .admin:
    print("Full access")
case .editor:
    print("Edit access")
case .viewer:
    print("Read-only access")
}
```

This is better because future readers can see exactly how each enum case is treated.

### Practice 4: Add raw values only when they serve a real purpose

Raw values are useful for serialization, display mapping, or interoperability. If you do not need those things, a simple enum is often cleaner.

A less-preferred approach adds raw values without any real need:

```swift
enum Theme: String {
    case light
    case dark
}
```

If you only need to represent a choice in code, this may be enough:

```swift
enum Theme {
    case light
    case dark
}
```

This keeps the enum simpler unless raw values are needed for storage, APIs, or user-facing text.

## 8. Limitations and Edge Cases

Enums are powerful, but they are not the right solution for every situation. These real-world limitations help you decide when to use them and when to choose another type.

- Enums model one choice or one state at a time. If you need many independent stored properties, a `struct` or `class` is often a better fit.
- Raw values must all be of the same raw type, such as `String` or `Int`. You cannot mix raw value types in one enum.
- Associated values are flexible, but they can make switching more verbose because you often need pattern matching to extract data.
- Creating an enum from a raw value with `EnumName(rawValue:)` returns an optional, so code may seem like it is “not working” if you forget to unwrap the result.
- If you add a new case to an enum later, existing `switch` statements may stop compiling until they handle the new case. This is usually helpful, but it can affect many files in a large project.
- Enums with associated values do not automatically behave like simple labels. Two values of the same case can still be different if their associated data is different.
- Some APIs and persistence layers work more easily with raw-value enums than with associated-value enums, because associated values need custom encoding or decoding logic in more advanced scenarios.

> **Tip:** A good rule is: use an enum when you want to say “this value must be one of these known cases.” Use a struct when you want to say “this value has these properties.”

## 9. Practical Mini Project

Let’s build a small but complete example that uses several enum features together. This program models an order status system for a shopping app.

It uses:

- a simple enum for payment method
- an enum with associated values for order status
- a `switch` to display useful messages

Here is the full code:

```swift
enum PaymentMethod {
    case card
    case cash
    case bankTransfer
}

enum OrderStatus {
    case pending
    case paid(PaymentMethod)
    case shipped(String)
    case delivered
    case cancelled(String)
}

func printOrderUpdate(status: OrderStatus) {
    switch status {
    case .pending:
        print("Your order has been received.")
    case .paid(let method):
        switch method {
        case .card:
            print("Payment confirmed by card.")
        case .cash:
            print("Payment will be collected in cash.")
        case .bankTransfer:
            print("Payment confirmed by bank transfer.")
        }
    case .shipped(let trackingNumber):
        print("Your order was shipped. Tracking number: \(trackingNumber)")
    case .delivered:
        print("Your order has been delivered.")
    case .cancelled(let reason):
        print("Order cancelled: \(reason)")
    }
}

let firstOrder = OrderStatus.paid(.card)
let secondOrder = OrderStatus.shipped("TRK-2048")
let thirdOrder = OrderStatus.cancelled("Item out of stock")

printOrderUpdate(status: firstOrder)
printOrderUpdate(status: secondOrder)
printOrderUpdate(status: thirdOrder)
```

Output (tested with Swift 6.4):

```text
Payment confirmed by card.
Your order was shipped. Tracking number: TRK-2048
Order cancelled: Item out of stock
```

This mini project shows why enums are so useful in Swift. The order can only be in one valid state at a time, and cases like `shipped` or `cancelled` can carry the exact extra data they need.

It also shows how nested switching can keep your logic very explicit. Instead of checking many unrelated booleans or strings, the enum itself describes the possible states of the order.

## 10. Key Points

- An enum defines a type with a fixed set of possible cases.
- Enums are ideal when a value should be one of several known choices.
- Swift enums can be much more powerful than simple labels because they support methods, computed properties, raw values, and associated values.
- Raw values are fixed values assigned to cases, while associated values store extra data in a specific enum instance.
- `switch` statements over enums are usually exhaustive, which helps catch missing logic at compile time.
- Enums are often better than strings for roles, states, modes, and categories because they reduce invalid values.
- If you need a type with many independent stored properties, a struct is often a better choice than an enum.

## 11. Practice Exercise

Try building your own enum-based weather status system.

- Create an enum named `Weather`.
- Add these cases: `sunny`, `cloudy`, `rainy(Int)`, and `snowy(Int)`.
- Write a function named `describeWeather` that takes a `Weather` value.
- Use a `switch` to print a different message for each case.
- For `rainy` and `snowy`, print the amount using the associated value.

**Expected output:** Messages such as `Sunny day`, `Rainfall: 12 mm`, or `Snowfall: 5 cm`.

**Hint:** Use pattern matching with `let` inside the `switch` cases to extract the associated values.

```swift
enum Weather {
    case sunny
    case cloudy
    case rainy(Int)
    case snowy(Int)
}

func describeWeather(weather: Weather) {
    switch weather {
    case .sunny:
        print("Sunny day")
    case .cloudy:
        print("Cloudy sky")
    case .rainy(let amount):
        print("Rainfall: \(amount) mm")
    case .snowy(let amount):
        print("Snowfall: \(amount) cm")
    }
}

let today = Weather.rainy(12)
describeWeather(weather: today)
```

Output (tested with Swift 6.4):

```text
Rainfall: 12 mm
```

## 12. Final Summary

Swift enumerations let you model values that must belong to a limited set of valid cases. That makes your code clearer, safer, and easier to reason about than using loose strings, numbers, or flag variables. In this article, you saw how enums work, how to declare cases, how to use raw values and associated values, and how to process enums with exhaustive `switch` statements.

You also saw when enums are a great fit and when a struct may be better. If your data represents one choice, one mode, or one state from a known list, an enum is often the right tool. If your data needs several stored properties at the same time, a struct is usually more natural.

A strong next step is to learn more about Swift pattern matching and `switch` statements, because those features make enums even more powerful. After that, explore structs, methods on custom types, and protocol conformance to build richer Swift models.

## Where AI Coding Assistants Get This Wrong

- Often suggested: Tests a payload case with `if result == .failure { ... }`.
  Instead: Use pattern matching: `if case .failure = result`, `if case let .failure(message) = result`, or `guard case` for early exit. (Without `Equatable` the `==` doesn't compile, and even with it `.failure` isn't a complete value; the compiler asks for the associated `String`.)
- Often suggested: Hand-writes `init(from:)`/`encode(to:)` for every enum with associated values, or assumes the synthesized `Codable` emits a `{"type": "circle", ...}` discriminator.
  Instead: Declare `Codable` and let Swift 5.5+ synthesize it; it produces `{"circle":{"radius":2}}`. Write custom coding only when an external API dictates a different shape, such as a `type` field. (SE-0295's case-keyed format works for your own persistence but rarely matches third-party JSON, so you need to decide deliberately which one you want.)
- Often suggested: Switches exhaustively over an SDK enum such as `URLSessionTask.State` with no `@unknown default`, or silences the diagnostic with a plain `default:`.
  Instead: List the known cases and finish with `@unknown default:`. (The missing clause is an error in Swift 6 mode, and a plain `default` suppresses the warning you'd otherwise get when the SDK adds a case.)
