This post explains how a breathing app called Lunair uses an explicit state machine to manage complex session state in Swift/iOS.
•Boolean flags like isPlaying and isPaused led to impossible states, so the author switched to a Swift enum with cases: idle, preparing, active, paused, completing, and completed
•All state transitions go through a single transition() method that validates moves using isValidTransition(), catching illegal transitions with assertionFailure during development
•Side effects such as haptics, timers, and analytics are centralized in handleSideEffects() rather than scattered across the UI layer
•Scene phase changes (backgrounding, phone calls) auto-pause the session by saving the current breath phase and cycle count into the paused case, but do not auto-resume
•
The author notes they would adopt the newer @Observable framework today, but considers the state machine the most stabilizing architectural decision in the app
This summary was automatically generated by AI based on the original article and may not be fully accurate.