How to Iterate Over Enum Cases in Swift

How to Iterate Over Enum Cases in Swift

Written by

in

Quick answer: To iterate over an enum in Swift, conform the enum to the CaseIterable protocol. Swift then automatically synthesizes a static allCases collection, allowing you to loop through instances using for item in MyEnum.allCases.

Iterating over every case in a Swift enumeration is a common task, whether you are populating a SwiftUI Picker, displaying options in a list, or validating user input against a known set of raw values. However, writing for suit in Suit directly against an enum declaration results in a compiler error.

Type 'Suit' does not conform to protocol 'Sequence'

Here is why this occurs and how to safely loop over raw-value enums and custom enumerations across modern Swift versions.

Why this happens

In Swift, an enum defines a discrete named type, not a collection. Unlike arrays or sets, an enum type does not conform to Sequence or Collection out of the box. The Swift compiler does not expose cases as an iterable list by default because enums can be non-trivial—they may carry associated values, have recursive definitions (indirect), or represent states that cannot be enumerated deterministically.

When you attempt to pass an enum type itself to a for-in loop, the compiler attempts to resolve the type as a sequence of items. Because the static metatype Suit.Type does not provide an iterator or conform to Sequence, compilation halts immediately.

To make an enum iterable, Swift provides the standard library protocol CaseIterable. For standard enums without associated values, the compiler automatically synthesizes an allCases collection matching the declaration order of your cases.

How to fix it

1. Conform to the CaseIterable Protocol

The most idiomatic and maintainable way to iterate over an enum is to declare conformance to CaseIterable. You can add this directly to your enum definition alongside any raw value type such as String or Int.

import Foundation

enum Suit: String, CaseIterable {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

// Iterate using a standard for-in loop
for suit in Suit.allCases {
    print("\(suit): \(suit.rawValue)")
}

When CaseIterable is added, Swift automatically generates the following static property behind the scenes:

static var allCases: [Suit] { get }

You can also use functional collection methods such as forEach, map, or filter on allCases:

Suit.allCases.forEach { suit in
    print(suit.rawValue)
}

let allSymbols = Suit.allCases.map { $0.rawValue }
// ["♠", "♥", "♦", "♣"]

2. Extend an Existing or External Enum

If you are working with an enum defined in an external framework, system library, or a separate module where you cannot modify the original declaration, you can declare conformance in an extension.

// Assume DeviceState is declared in another module without CaseIterable
extension DeviceState: CaseIterable {}

As long as the enum does not have associated values and all cases are visible in the current compilation context, the compiler will synthesize allCases automatically.

3. Use CaseIterable Enums in SwiftUI Pickers

A frequent real-world use case for enum iteration is generating interactive UI components in SwiftUI. Combine CaseIterable with Identifiable so your view can uniquely identify each option without explicit id: \.self tags.

import SwiftUI

enum Priority: String, CaseIterable, Identifiable {
    case low = "Low"
    case medium = "Medium"
    case high = "High"

    var id: String { rawValue }
}

struct TaskPriorityView: View {
    @State private var selectedPriority: Priority = .medium

    var body: some View {
        Form {
            Picker("Priority", selection: $selectedPriority) {
                ForEach(Priority.allCases) { priority in
                    Text(priority.rawValue).tag(priority)
                }
            }
        }
    }
}

4. Provide a Manual allCases Implementation for Enums with Associated Values

The Swift compiler cannot automatically synthesize allCases if any case contains associated values, because an associated value represents an infinite or runtime-dependent set of possibilities. In this scenario, you must conform to CaseIterable manually by supplying your own allCases array.

enum NetworkRoute: CaseIterable {
    case home
    case profile(userID: String)
    case settings

    // Define representative cases for iteration
    static var allCases: [NetworkRoute] {
        return [
            .home,
            .profile(userID: "default"),
            .settings
        ]
    }
}

for route in NetworkRoute.allCases {
    print(route)
}

5. Exclude Deprecated or Internal Cases

By default, automatic synthesis includes every declared case. If your enum includes legacy cases marked with @available(*, unavailable) or internal debugging states that should not appear in production iteration, implement allCases manually to restrict which values are returned.

enum AppTheme: String, CaseIterable {
    case system
    case light
    case dark
    case highContrastBeta

    static var allCases: [AppTheme] {
        // Exclude experimental or hidden cases from generic UI loops
        return [.system, .light, .dark]
    }
}

How to prevent it

  • Add CaseIterable during initial model design for any enum intended to populate pickers, segmented controls, or filter lists.
  • Do not iterate over enums by instantiating raw values sequentially in a while loop (such as checking MyEnum(rawValue: i)); this pattern fails as soon as raw values become non-contiguous.
  • When adding cases with associated values to an existing CaseIterable enum, be aware that automatic synthesis will immediately break and trigger a build error until you implement static var allCases manually.
  • If an enum represents an infinite or unbounded domain, avoid conforming to CaseIterable to prevent misleading consumer code that assumes the list is exhaustive.

FAQ

Can I iterate over an enum without raw values?

Yes. CaseIterable works on simple enums regardless of whether they have raw values. An enum declared as enum Direction: CaseIterable { case north, south, east, west } will synthesize allCases containing all four cases.

What order does allCases preserve?

The synthesized allCases collection preserves the exact source order in which the cases are written in the enum declaration from top to bottom.

Why does Xcode show ‘Type does not conform to protocol CaseIterable’?

This error occurs if your enum contains one or more cases with associated values, or if you declare conformance in an extension located in a different file from the original enum definition. For enums with associated values, you must provide your own explicit static var allCases implementation.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.