Elegant state machine for Swift.
enum MyState: StateType {
case State0, State1, State2
}
// setup state machine
let machine = Machine<MyState, NoEvent>(state: .State0) { machine in
machine.addRoute(.State0 => .State1)
machine.addRoute(.Any => .State2) { context in print("Any => 2, msg=\(context.userInfo)") }
machine.addRoute(.State2 => .Any) { context in print("2 => Any, msg=\(context.userInfo)") }
// add handler (`context = (event, fromState, toState, userInfo)`)
machine.addHandler(.State0 => .State1) { context in
print("0 => 1")
}
// add errorHandler
machine.addErrorHandler { event, fromState, toState, userInfo in
print("[ERROR] \(transition.fromState) => \(transition.toState)")
}
}
// initial
XCTAssertTrue(machine.state == .State0)
// tryState 0 => 1 => 2 => 1 => 0
machine <- .State1
XCTAssertTrue(machine.state == .State1)
machine <- (.State2, "Hello")
XCTAssertTrue(machine.state == .State2)
machine <- (.State1, "Bye")
XCTAssertTrue(machine.state == .State1)
machine <- .State0 // fail: no 1 => 0
XCTAssertTrue(machine.state == .State1)
This will print:
0 => 1
Any => 2, msg=Optional("Hello")
2 => Any, msg=Optional("Bye")
[ERROR] State1 => State0
Use <-!
operator to try transition by Event
rather than specifying target State
(Test Case).
enum MyEvent: EventType {
case Event0, Event1
}
let machine = StateMachine<MyState, MyEvent>(state: .State0) { machine in
// add 0 => 1 => 2
machine.addRoute(event: .Event0, transitions: [
.State0 => .State1,
.State1 => .State2,
])
}
// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State1)
// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State2)
// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any")
If there is no Event
-based transition, use built-in NoEvent
instead.
For more examples, please see XCTest cases.
- Easy Swift syntax
- Transition:
.State0 => .State1
,[.State0, .State1] => .State2
- Try transition:
machine <- .State1
- Try transition + messaging:
machine <- (.State1, "GoGoGo")
- Try event:
machine <-! .Event1
- Transition:
- Highly flexible transition routing
- Using
Condition
- Using
.Any
state/event - Blacklisting:
.Any => .Any
+Condition
- Route Mapping (closure-based routing): #36
- Using
- Success/Error/Entry/Exit handlers with
order: UInt8
(more flexible than before/after handlers) - Removable routes and handlers
- Chaining:
.State0 => .State1 => .State2
- Hierarchical State Machine: #10
Term | Type | Description |
---|---|---|
State | StateType (protocol) |
Mostly enum, describing each state e.g. .State0 . |
Event | EventType (protocol) |
Name for route-group. Transition can be fired via Event instead of explicitly targeting next State . |
State Machine | Machine |
State transition manager which can register Route /RouteMapping and Handler separately for variety of transitions. |
Transition | Transition |
From- and to- states represented as .State1 => .State2 . Also, .Any can be used to represent any state. |
Route | Route |
Transition + Condition . |
Condition | Context -> Bool |
Closure for validating transition. If condition returns false , transition will fail and associated handlers will not be invoked. |
Event Route Mapping | (event: E?, fromState: S, userInfo: Any?) -> S? |
Another way of defining routes using closure instead of transition arrows (=> ). This is useful when state & event are enum with associated values. Return value (S? ) means preferred-toState , where passing nil means no routes available. See #36 for more info. |
State Route Mapping | (fromState: S, userInfo: Any?) -> [S]? |
Another way of defining routes using closure instead of transition arrows (=> ). This is useful when state is enum with associated values. Return value ([S]? ) means multiple toState s from single fromState . See #36 for more info. |
Handler | Context -> Void |
Transition callback invoked when state has been changed successfully. |
Context | (event: E?, fromState: S, toState: S, userInfo: Any?) |
Closure argument for Condition & Handler . |
Chain | TransitionChain / RouteChain |
Group of continuous routes represented as .State1 => .State2 => .State3 |
- Swiftで有限オートマトン(ステートマシン)を作る - Qiita (Japanese)
- Swift+有限オートマトンでPromiseを拡張する - Qiita (Japanese)