State machines in Swift

September 25, 2016

The problem

  • 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:

States:
  • Configuring
  • Counting Down
  • Alarming
  • Snoozing

Actions:
  • 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 if and 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

With iOS 9 and OS X 10.11, a new framework called GameplayKit was released. It includes a robust state machine implementation!

Good results

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!

Resources

comments powered by Disqus