Swift: Swift Enums and Pattern Matching Tutorial

Enums in Swift are far more than "a set of constants" -- they are first-class citizens that can carry data, support methods, and even define themselves recursively. They are the best tool for building state machines and result types.

1. What You'll Learn


2. A Mobile Developer's Real Story

(1) Pain Point: Hard to Express API Response States

Alice ran into a tricky problem while building the networking layer for a news app. An API request could return multiple results:

SWIFT
// Inelegant approach: multiple optional variables to represent state
var data: NewsResponse? = nil
var error: Error? = nil
var isLoading = false

This approach has three problems: ambiguous meaning when data and error are both present, forgetting to handle a state causes crashes, and scattered logic is hard to maintain.

(2) The Enum with Associated Values Solution

Use a single enum to unify all states, with each case carrying its own data:

SWIFT
enum NetworkResult<T> {
    case success(T)
    case failure(Error)
    case loading
}

NetworkResult<[NewsArticle]> either carries article data (success), error information (failure), or the loading state -- three mutually exclusive states with clear semantics.

(3) Benefits: State Management from Chaos to Clarity

Dimension Multiple Optional Variables Enum with Associated Values
State count 2^N illegal combinations N precise states
Semantic clarity data/error may coexist Each state carries its own data
Handling completeness Easy to miss cases switch exhaustiveness check
Code maintainability Scattered in if-else Centralized in one type

3. Enum Basics

Enums define a set of related values. Swift enums are more flexible than those in other languages -- they can associate methods, computed properties, and subscripts.

(1) Basic Enums and Raw Values

100%
graph LR
    A["enum Compass {\n case north\n case south\n case east\n case west\n}"] --> B["Compass.north"]
    A --> C["Compass.south"]
    A --> D["Compass.east"]
    A --> E["Compass.west"]

A raw value is a fixed value behind each enum case, which can be of types like String or Int.

Raw Value Type Declaration Value
Int enum Status: Int { case ok = 200 } Integer
String enum Direction: String { case north = "N" } String
Implicit Int enum Grade: Int { case a, b, c } Increments from 0
Implicit String enum Code: String { case red, green } Same as case name

▶ Example: HTTP Status Code Enum

SWIFT
// ============================================
// HTTP status code enum using raw values
// ============================================
enum HTTPStatus: Int {
    case ok = 200
    case created = 201
    case badRequest = 400
    case unauthorized = 401
    case notFound = 404
    case serverError = 500
}
let status = HTTPStatus.notFound
print("Code: \(status.rawValue)")
print("Name: \(status)")
// Create from raw value
let parsed = HTTPStatus(rawValue: 200)
print("Parsed: \(parsed ?? .serverError)")

Output:

TEXT 📖 Display only
Code: 404
Name: notFound
Parsed: ok

(2) Enum Methods and Computed Properties

Enums can define methods and properties just like structs.

▶ Example: Enum with Methods

SWIFT
// ============================================
// Custom enum method: determine status category
// ============================================
enum HTTPStatus: Int {
    case ok = 200
    case created = 201
    case badRequest = 400
    case unauthorized = 401
    case notFound = 404
    case serverError = 500
    var isSuccess: Bool {
        rawValue >= 200 && rawValue < 300
    }
    var description: String {
        switch self {
        case .ok: return "OK"
        case .created: return "Created"
        case .badRequest: return "Bad Request"
        case .unauthorized: return "Unauthorized"
        case .notFound: return "Not Found"
        case .serverError: return "Internal Server Error"
        }
    }
}
let code = HTTPStatus.ok
print("\(code.rawValue) \(code.description) - Success: \(code.isSuccess)")

Output:

TEXT 📖 Display only
200 OK - Success: true

4. Associated Values and Pattern Matching

Associated values are a core feature that sets Swift enums apart from other languages -- each case can carry additional data of different types.

(1) Associated Value Syntax

100%
graph TB
    A["NetworkResult<T>"] --> B["success(T) - carries data"]
    A --> C["failure(Error) - carries error"]
    A --> D["loading - no data"]
    B --> E["switch result {\n case .success(let data):\n   show(data)\n case .failure(let error):\n   show(error)\n case .loading:\n   showSpinner()\n}"]
Feature Raw Values Associated Values
Value type Fixed constant Variable data
Storage timing Determined at definition Passed at creation
Per case All cases have same value type Each case can differ
Use case Mapping fixed codes Carrying dynamic data

▶ Example: Handling Network Request Results

SWIFT
// ============================================
// Associated value enum for handling API responses
// ============================================
enum NetworkResult<T> {
    case success(T)
    case failure(String)
    case loading
}
func handleResponse<T>(_ result: NetworkResult<T>) {
    switch result {
    case .success(let data):
        print("Successfully received data: \(data)")
    case .failure(let error):
        print("Request failed: \(error)")
    case .loading:
        print("Loading...")
    }
}
handleResponse(NetworkResult.success("User Info"))
handleResponse(NetworkResult.failure("Network connection timed out"))
handleResponse(NetworkResult.loading)

Output:

TEXT 📖 Display only
Successfully received data: User Info
Request failed: Network connection timed out
Loading...

(2) if-case and guard-case Pattern Matching

Besides switch, you can use if case and guard case for single-case matching.

▶ Example: Concise Single-Case Matching

SWIFT
// ============================================
// Concise usage of if-case and guard-case
// ============================================
enum Measurement {
    case weight(Double)
    case height(Double)
    case count(Int)
}
let record = Measurement.weight(75.5)
// if-case: only care about one case
if case .weight(let kg) = record, kg > 70 {
    print("Weight exceeds 70kg: \(kg)")
}
// guard-case: early exit
func process(_ m: Measurement) {
    guard case .count(let n) = m else {
        print("Not a count type")
        return
    }
    print("Count is \(n)")
}
process(.weight(65))
process(.count(42))

Output:

TEXT 📖 Display only
Weight exceeds 70kg: 75.5
Not a count type
Count is 42

5. Recursive Enums

A recursive enum is one whose associated values reference the enum itself, suitable for expressing tree-like or nested structures.

(1) The indirect Keyword

Use indirect to mark a recursive case or the entire enum, telling the compiler that indirect storage is needed.

Syntax Description
indirect case expression(Expression) Single recursive case
indirect enum Expression { ... } Entire enum allows recursion

▶ Example: Evaluating a Math Expression

SWIFT
// ============================================
// Recursive enum: math expression tree
// ============================================
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}
func evaluate(_ expr: ArithmeticExpression) -> Int {
    switch expr {
    case .number(let value):
        return value
    case .addition(let left, let right):
        return evaluate(left) + evaluate(right)
    case .multiplication(let left, let right):
        return evaluate(left) * evaluate(right)
    }
}
// Build the expression (3 + 5) * 2
let three = ArithmeticExpression.number(3)
let five = ArithmeticExpression.number(5)
let sum = ArithmeticExpression.addition(three, five)
let product = ArithmeticExpression.multiplication(sum, .number(2))
print("(3 + 5) * 2 = \(evaluate(product))")

Output:

TEXT 📖 Display only
(3 + 5) * 2 = 16

6. Full Example: Order State Machine

SWIFT
// ============================================
// Full example: E-commerce order state machine
// Features: order state transitions + data processing
// ============================================
import Foundation
// 1. Order status enum (with associated values)
enum OrderStatus {
    case pending          // Pending payment
    case paid(Date)       // Paid (with payment time)
    case shipped(trackingNumber: String)  // Shipped (with tracking number)
    case delivered(Date)  // Delivered (with receipt time)
    case cancelled(reason: String)  // Cancelled (with reason)
    var description: String {
        switch self {
        case .pending: return "Pending"
        case .paid(let date): return "Paid (\(date.formatted()))"
        case .shipped(let tracking): return "Shipped (Tracking: \(tracking))"
        case .delivered(let date): return "Delivered (\(date.formatted()))"
        case .cancelled(let reason): return "Cancelled: \(reason)"
        }
    }
    var isFinalState: Bool {
        if case .delivered = self { return true }
        if case .cancelled = self { return true }
        return false
    }
}
// 2. Usage example
let now = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let statuses: [OrderStatus] = [
    .pending,
    .paid(now),
    .shipped(trackingNumber: "SF1234567890"),
    .delivered(now),
    .cancelled(reason: "Cancelled by user")
]
for status in statuses {
    let final = status.isFinalState ? " (Final)" : ""
    print("\(status.description)\(final)")
}

Output:

TEXT 📖 Display only
Pending
Paid (2026-07-30)
Shipped (Tracking: SF1234567890)
Delivered (2026-07-30) (Final)
Cancelled: Cancelled by user (Final)

❓ FAQ

Q How do I choose between enums and structs?
A Enums suit "mutually exclusive states" -- at any point only one can be active. Structs suit "multiple coexisting properties" -- all properties exist simultaneously. The choice depends on whether your data model is an OR relationship or an AND relationship.
Q Can associated values and generics be used together?
A Yes. enum Optional<T> is a classic example of associated values + generics: case none or case some(T). Associated values can reference generic parameters.
Q Why are Swift enums more powerful than C/Java enums?
A Swift enums can have methods, computed properties, associated values, recursive definitions, and protocol conformance. C/Java enums are merely named integer constants; Swift enums are full first-class types.
Q When must I use indirect?
A When an enum's associated value type references itself. For example, ArithmeticExpression.addition has associated values of type ArithmeticExpression, forming a recursive reference that requires indirect.
Q Must I always write default in a switch on an enum?
A It's recommended to exhaust all cases and NOT use default. This way, when you add a new case in the future, the compiler warns you about missing cases, preventing accidental omissions. But if you genuinely don't need to handle all cases, you can add default.

📖 Summary


📝 Exercises

  1. Basic: Define a TrafficLight enum with three cases: red, yellow, green. Add a duration computed property that returns the duration in seconds for each light.
  2. Intermediate: Use associated values to define an Either<L, R> enum (similar to Rust's Result) with left(L) and right(R) cases, and add a map method to transform the right value.
  3. Challenge: Use a recursive enum to implement a JSON type (supporting null, bool, int, string, array, dictionary), and write a function to format a JSON enum value as an indented string.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏