Architecture • 23 June 2026 • Written by Lochan Chugh

Building Predictable State Machines with Swift Macros

Building Predictable State Machines with Swift Macros

Building Predictable State Machines with Swift Macros

One of the greatest challenges in iOS architecture is preventing “Impossible States”—like a view showing both a loading spinner and an error message. Swift Macros provide a powerful new tool to automate the generation of robust, boilerplate-free state machines.

The Hook: The Enum Boilerplate

Implementing a strict state machine usually involves massive switch statements and repetitive transition logic. This boilerplate is not only tedious but also a breeding ground for bugs when a developer forgets to handle a specific edge case in one of the many transition methods.

The “Why”: Compile-Time Enforcement

By using an @Attached macro, we can generate valid transition methods automatically. If a transition from Loading to Success is defined, the macro generates the code; if a transition from Success back to Loading isn’t defined, the code won’t even compile.

The Implementation: The @StateMachine Macro

Imagine a macro that transforms a simple enum into a fully functional state machine.

@StateMachine
enum ViewState {
    case idle
    case loading
    case success(data: [String])
    case error(message: String)
    
    // Macro generates:
    // func toLoading() -> ViewState
    // func toSuccess(data: [String]) -> ViewState
    // var isLoading: Bool { get }
}

// Usage in a ViewModel
@Observable
class UserViewModel {
    var state: ViewState = .idle
    
    func fetchData() async {
        state = state.toLoading() // Safe, generated transition
        do {
            let data = try await API.fetch()
            state = state.toSuccess(data: data)
        } catch {
            state = state.toError(message: error.localizedDescription)
        }
    }
}

The macro ensures that you cannot accidentally move from idle to success without passing through loading, if that’s how your business logic is defined.

The Verdict: Clarity vs. Complexity

  • Pros: Eliminates boilerplate; ensures consistency across the team; makes invalid states unrepresentable.
  • Cons: Increases build times; requires the team to learn the Macro DSL.
  • When to use: Complex flows like authentication, multi-step forms, or media playback.

Internal Connectivity

External Resources

Ready for more depth?

Master these concepts with our structured technical roadmap.

View Roadmap