Using Enums with Switch/Case Statements in Swift

Enums and switch statements are a natural pair in Swift because the compiler can help you handle every possible value.
Written by

Iñaki Narciso

Updated on

Apr 06 2026

Table of contents

    Overview

    One of the strengths of using switch in Swift is pattern matching. When the value you are switching over is an enum, Swift can check whether your switch handles every possible case.

    That compiler check is called exhaustiveness. It is one reason enums and switch statements work so well together: you describe the possible values in one place, then Swift helps you remember to handle them in your logic.

    Why Enums Work So Well with Switch

    An enum defines a fixed set of possible values. For example, here is an enum for the planets in our solar system:

    enum Planet {
        case mercury
        case venus
        case earth
        case mars
        case jupiter
        case saturn
        case uranus
        case neptune
    }

    Because Swift knows every possible Planet, it can check whether a switch statement covers them all.

    An Incomplete Enum Switch

    Here is a switch that only handles three planets:

    let planet: Planet = .earth
    
    switch planet {
    case .mercury:
        print("The planet closest to the sun")
    case .earth:
        print("This is where we live")
    case .neptune:
        print("The planet farthest from the sun")
    }

    This will not compile. Swift will show an error like:

    Switch must be exhaustive

    That message means the switch does not handle every possible value of Planet. In this case, venus, mars, jupiter, saturn, and uranus are missing.

    Option 1: Add the Missing Cases

    The safest fix is to add a case for every remaining enum value:

    switch planet {
    case .mercury:
        print("The planet closest to the sun")
    case .venus:
        print("The second planet from the sun")
    case .earth:
        print("This is where we live")
    case .mars:
        print("The red planet")
    case .jupiter:
        print("The largest planet")
    case .saturn:
        print("The planet with famous rings")
    case .uranus:
        print("An ice giant")
    case .neptune:
        print("The planet farthest from the sun")
    }

    Xcode can help here. When the compiler complains that a switch must be exhaustive, its fix-it can add missing cases for you. You still need to fill in the behavior for each case, but the compiler gives you the checklist.

    Option 2: Use Default

    You can also add a default case:

    switch planet {
    case .mercury:
        print("The planet closest to the sun")
    case .earth:
        print("This is where we live")
    case .neptune:
        print("The planet farthest from the sun")
    default:
        print("A planet inside the solar system")
    }

    This compiles because default catches everything that was not matched above.

    Quick rule of thumb: Prefer explicit enum cases when every value deserves its own behavior. Use default when all remaining cases truly share the same behavior.

    Why Explicit Cases Are Usually Safer

    A default case can be convenient, but it also hides future changes. Imagine you add a new enum case later:

    enum Planet {
        case mercury, venus, earth, mars
        case jupiter, saturn, uranus, neptune
        case pluto
    }

    If your switch handles every case explicitly, Swift will tell you which switches need updating. If your switch uses default, the new case quietly falls into the default behavior.

    That is not always wrong, but it is something to choose intentionally.

    A Practical App Example

    Enums are especially useful for app states because a screen is usually in one clear state at a time.

    enum LoadingState {
        case idle
        case loading
        case loaded([String])
        case failed(String)
    }
    
    let state = LoadingState.loaded(["Mercury", "Venus", "Earth"])
    
    switch state {
    case .idle:
        print("Ready to load")
    case .loading:
        print("Loading...")
    case .loaded(let planets):
        print("Loaded \\(planets.count) planets")
    case .failed(let message):
        print("Error: \\(message)")
    }

    This switch is exhaustive and it also extracts associated values with let. The loaded case gives you the array, and the failed case gives you the error message.

    Common Mistakes

    Using default too early

    If you are still designing the enum, avoid adding default just to silence the compiler. Let the compiler show you what is missing.

    Forgetting that enum cases can carry values

    Enum cases can store associated values. When they do, use pattern matching to extract the values inside the switch.

    Writing the same code in every case

    If many cases do the same thing, combine them with commas or use a carefully chosen default.

    Quick Reference

    PatternWhat It Does
    case .earth:Matches one specific enum case
    case .venus, .mars:Matches either of the listed enum cases
    default:Matches anything that was not handled earlier
    case .loaded(let items):Matches a case with an associated value and stores it in a local constant
    case let .failed(message):Another common syntax for extracting an associated value

    Summary

    • Enums define a fixed set of possible values.
    • Switch statements are a strong fit for enums because Swift can check exhaustiveness.
    • If a switch over an enum misses cases, Swift shows the Switch must be exhaustive compiler error.
    • Adding every missing case is usually safer than relying on default.
    • Use default when all remaining cases truly share the same behavior.
    • Switch statements can extract associated values from enum cases.

    That is the core idea: enums describe the possible states, and switch statements describe what should happen for each state. When you use them together, Swift can help keep your code honest as your app grows.