State Machine
/*
Simple State Machine in Swift
https://blog.lukaskukacka.com/swift/2018/10/08/simple-state-machine-in-swift.html
*/
public protocol StateMachine {
/// represents a State of a state machine.
/// this will most likely be an enum.
/// it is `Hashable` so it can be stored in a `Set`
/// of allowed state transitions.
associatedtype State: Hashable
/// current state of a state machine
var state: State { get set }
/// states transitions dictionary.
/// contains set of allowed next states for each state.
static var transitions: [State: Set<State>] { get }
/// determines whether the transition to next state is allowed
/// from current state.
func canTransition(to nextState: State) -> Bool
/// performs the transition of a state machine to next state.
/// it is marked as mutating because it mutates the state.
mutating func transition(to nextState: State)
}
// default implementation
extension StateMachine {
// self.canTransition(to: nextState)
func canTransition(to nextState: State) -> Bool {
return Self.transitions[state]?.contains(nextState) ?? false
}
// self.transition(to: nextState)
mutating func transition(to nextState: State) {
guard canTransition(to: nextState) else {
print("\nโ Invalid state transition: \n\t\(state) -> \(nextState)")
return
}
print("\n\(type(of: self)) is about to change state:\n\t\(state) -> \(nextState)")
self.state = nextState
}
}
struct JiraTicket: StateMachine {
let id: String
init(id: String) {
self.id = id
}
// `StateMachine` protocol requirements
var state: State = .open
enum State: CaseIterable {
case open
case reopened
case inProgress
case resolved
case closed
}
static let transitions: [State: Set<State>] = [
.open : [.inProgress, .resolved, .closed],
.reopened : [.inProgress, .resolved, .closed],
.inProgress: [.open, .resolved],
.resolved : [.reopened, .closed],
.closed : [.reopened]
]
}
var ticket = JiraTicket(id: "XX-123")
// open -> inProgress
ticket.transition(to: .inProgress)
// inProgress -> resolved
ticket.transition(to: .resolved)
// โ Invalid state transition
ticket.transition(to: .inProgress)
Last updated