- You have a large and complicated if/switch statement
- You keep copying parts of the if/switch block around the codebase
- You keep needing to add/tweak the conditions for edge cases
If you run into issues like these, it might be a valid use case for a finite-state machine. A state machine can help you simplify the code, and make the logic more explicit!
My experience of the issue
When working on my app Nap Slide I ran into an issue handling transitions between alarm clock states. An alarm clock seems like it should be pretty simple to implement, but when you look at the possible states and transitions things get pretty complicated:
- Counting Down
- Start Countdown
- Cancel Nap (and current configuration)
- Cancel Alarm
- Start Alarming
- Start Snoozing
At first, I tried to use a couple of Enums to represent the alarm state, and started using
switch blocks to handle transitions to new states. This got ugly really quickly, and the code was hard to understand. I wasted a couple days trying to find all the edge cases, and felt like I was patching holes in a sinking ship.
What state machines do
State machines give you an explicit way to declare possible states, exactly what state(s) can transition to another state, and what to do when a transition happens. This is all possible with conditionals, but things get so complicated and hard to reason about that state machines are usually a better solution.
Summary of benefits:
- easier to reason about since behavior is defined in one place rather than scattered throughout the codebase
- code about the behavior of a state is now in one place
- transitions between states are explicitly declared
- invalid transitions between states are no longer possible
Using state machines in Swift
After implementing the state machine - the code became much easier to understand and modify. The strange edge cases I found when testing and failed to resolve; were all gone and my problem was solved!