Simple State Machine in Swift
by Lukas Kukacka
Our programs often use State Machines even though we might not even realise it.
How often do we have something called State and associated rules how states can change? Well there we most likely have a State Machine.
Jira Ticket example
Let’s take a Ticket in issue tracking tool Jira as an example of a State Machine. Each Ticket can have multiple states and states can change only following a set of rules according to Default workflow diagram.
Ticket can transition states following these rules:
- Open ticket cannot be Reopened.
- In Progress ticket cannot be Closed without first being Resolved.
- Resolved ticket cannot be put In Progress without first being Reopened.
- and so on…
Instead of writing all conditions here, it can be better expressed using the following diagram:
Image taken from Jira: Working with workflows
Show me the code
There are existing libraries for State Machine implementation in Swift, some of them very sophisticated and advanced. But often a simple solution is just enough…
StateMachine
protocol
A state machine is defined using StateMachine
protocol. Let’s have a closer look…
This is a type representing a State of a state machine. This will most likely be an enum
. It is Hashable
so it can be stored in Set
of allowed state transitions.
Accessor to current state of a state machine. Every state machine must have a state at all times.
This is the main magic behind our state machine: its state transition table. For each possible state, it defines what are the next states the state machine is allowed to transition to. It is basically code definition of the state diagram above.
Determines whether the transition to next state candidate is allowed from current state.
Performs the transition of a state machine to next state. It is marked as mutating
because it mutates the state.
Extension with default implementation
The extension provides default implementation of canTransition(to:)
. By default does the expected: state transition is allowed when stateTransitions
allows it.
Example implementation
The example implementation is pretty straightforward.
enum State
represents all possible states of a ticket.stateTransitions
is defined with all possible transitions.transition(to:)
performs the state transition.- It changes value of a private variable.
- There is a
precondition
to check if the transition is valid. This implementation considers invalid state transition being a programmer’s error.canTransition(to:)
should always be consulted first before changing the state.
Usage
Ticket can transition states when mutable (defined as var
).
Because the transition(to:)
is mutating
, it cannot be called on immutable ticket defined using let
.
Like the article, got a comment or found an issue? Get in touch via Twitter .
Subscribe via RSS