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

Swift enumerations, usually written as enum, let you define a fixed set of related values under one type. They are one of Swift’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.

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.

Anatomy of a Swift enum
enum Direction { case north }
enum
declaration keyword
Direction
type name
case
case keyword
north
case name

A Swift enum declares a type and the cases that belong to it.

Here is a simple enum declaration:

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:

Imagine you store order status as a string:

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.

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 or Core Idea

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:

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:

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:

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:

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.

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")
}

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".

enum Difficulty {
    case easy
    case medium
    case hard
}

let selectedDifficulty = Difficulty.medium
print(selectedDifficulty)

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.

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")
}

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.

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

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

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:

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.

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)")
}

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:

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:

enum PaymentMethod {
    case creditCard
    case bankTransfer
    case cash
}

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

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.

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.

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.

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.

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.

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.

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.

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.

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:

let userRole = "admim"

A better approach uses an enum:

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:

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

A better approach groups the state and its data together:

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:

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

A better approach spells out the cases:

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:

enum Theme: String {
    case light
    case dark
}

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

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.

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:

Here is the full code:

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)

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

11. Practice Exercise

Try building your own enum-based weather status system.

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.

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)

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.