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.

  • An enum defines a fixed list of allowed cases.
  • Each case belongs to the enum’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.

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:

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 · Swift 6.4
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 · Swift 6.4
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 · Swift 6.4
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 · Swift 6.4
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 · Swift 6.4
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
Compiler output✓ Tested · Swift 6.4
error: type 'TrafficLight' has no member '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")
}
Compiler output✓ Tested · Swift 6.4
error: switch must be exhaustive

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")
Compiler output✓ Tested · Swift 6.4
error: enum case 'error' has no associated values

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")
Compiler output✓ Tested · Swift 6.4
error: value of optional type 'Role?' must be unwrapped to a value of type 'Role'

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 · Swift 6.4
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.

Show solution
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 · Swift 6.4
Rainfall: 12 mm

Where AI Coding Assistants Get This Wrong

Patterns AI tools commonly produce for this topic, and what to use instead.

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.

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.