Swiftのcustom operatorを使って何かネタを書きたいなぁ・・と思っていたら、ふとステートマシン(A => B
に遷移、みたいな)が浮かんだので、試しにライブラリを作ってみました。
状態をenumで定義して、いろいろこねくり回せます。
コード例
enum MyState: StateType {
case State0, State1, State2
case AnyState // create case=Any
static func convertFromNilLiteral() -> MyState { return AnyState }
}
let machine = StateMachine<MyState, MyEvent>(state: .State0) { machine in
machine.addRoute(.State0 => .State1)
machine.addRoute(nil => .State2) { context in println("Any => 2, msg=\(context.userInfo!)") }
machine.addRoute(.State2 => nil) { context in println("2 => Any, msg=\(context.userInfo!)") }
// add handler (handlerContext = (event, transition, order, userInfo))
machine.addHandler(.State0 => .State1) { context in
println("0 => 1")
}
// add errorHandler
machine.addErrorHandler { (event, transition, order, userInfo) in
println("[ERROR] \(transition.fromState) => \(transition.toState)")
}
}
// tryState 0 => 1 => 2 => 1 => 0
machine <- .State1
machine <- (.State2, "Hello")
machine <- (.State1, "Bye")
machine <- .State0 // fail: no 1 => 0
println("machine.state = \(machine.state)")
出力:
0 => 1
Any => 2, msg=Hello
2 => Any, msg=Bye
[ERROR] 1 => 0
machine.state = 1
トランジションの連鎖
パスコード入力の簡易版。
enum InputKey: StateType
{
case None
// associated valueを使わないのは、それなりにワケあり
case Key0, Key1, Key2, Key3, Key4, Key5, Key6, Key7, Key8, Key9
case Any
static func convertFromNilLiteral() -> InputKey { return Any }
}
var success = false
let machine = StateMachine<InputKey, String>(state: .None) { machine in
// connect all states
machine.addRoute(nil => nil)
// success = true only when transitionChain 2 => 3 => 5 => 7 is fulfilled
machine.addRouteChain(.Key2 => .Key3 => .Key5 => .Key7) { context in
success = true
return
}
}
// tryState
machine <- .Key2
machine <- .Key3
machine <- .Key4
machine <- .Key5
machine <- .Key6
machine <- .Key7
XCTAssertFalse(success)
machine <- .Key2
machine <- .Key3
machine <- .Key5
machine <- .Key7
XCTAssertTrue(success)
その他、transition登録の解除や、Event(Name)ベースのトリガーにも対応しています。
OSXやiOSのような、UI寄りのアプリ(=無限の状態)にどれだけ使えるか分かりませんが、何か良い活用法などあればご一報 or Pull Requestを下さい!