前回のSwiftで有限オートマトン(ステートマシン)を作る - Qiitaの続きです。
ステートマシン(SwiftState)を実際に使うにあたり、何か分かりやすいサンプルを作った方が良いと思い、今回、JavaScriptでおなじみのPromiseクラスを再発明してみました。
Promiseについて
Promiseについては、こちらの記事が参考になります。
一般的なPromiseライブラリの状態遷移は、Pending
=> Fulfilled
or Rejected
の2パターンがあります。
が、機能としてややシンプルすぎる&n番煎じ感が否めないので、新たにProgress
、Pause
、Cancel
といったインターフェースを追加して、複雑な状態遷移にも耐えられる設計にしてみます。
今回、設計した状態遷移図
(Reject
とCancel
は本来、内部処理/外部処理として区別されるべきですが、簡便のため、同じRejected
終状態として扱っています)
SwiftStateの使い方
ステートマシンの使い方をざっと眺めてみます。
まず、上記の遷移図から、タスクの「状態」と「遷移イベント」を定義します。
public enum TaskState: StateType
{
case Paused
case Running
case Fulfilled
case Rejected
...
}
public enum TaskEvent: StateEventType
{
case Pause
case Resume
case Progress
case Fulfill
case Reject // also used in cancellation for simplicity
...
}
次に、ステートマシンを定義します。
// setup state machine
self.machine = StateMachine<TaskState, TaskEvent>(state: .Running) {
// $0 = machine
$0.addRouteEvent(.Pause, transitions: [.Running => .Paused])
$0.addRouteEvent(.Resume, transitions: [.Paused => .Running])
$0.addRouteEvent(.Progress, transitions: [.Running => .Running])
$0.addRouteEvent(.Fulfill, transitions: [.Running => .Fulfilled])
$0.addRouteEvent(.Reject, transitions: [.Running => .Rejected, .Paused => .Rejected])
$0.addEventHandler(.Resume) { context in
configuration.resume?()
return
}
$0.addEventHandler(.Pause) { context in
configuration.pause?()
return
}
...
}
これにより、self.machine
は初期状態.Running
から、イベント遷移(.Pause
/.Fulfill
/.Reject
)を経て別の状態に遷移し、ひとたび終状態(.Fulfilled
,.Rejected
)になると逆戻りできなくなります(状態遷移のバリデーション用として使えます)。
また、ハンドラは複数登録が可能なので、Promiseのように終状態が確定するまで複数ハンドリングをpendingする際にも、自前で配列を用意せず、ステートマシン側に持たせることができます。これによって、Promise側に持たせるコード量が少しだけ短縮できます。
今回作ったライブラリ
上記をふまえて、今回作ったPromiseライブラリはこちらです:
https://github.com/inamiy/SwiftTask
作成にあたり、PromiseKitやBolts-iOS、その他JavaScriptライブラリなどを参考にしています。
こちらも後日、Qiita記事にまとめていけたらと思うので、StarやPull Request、ツッコミ等あればよろしくお願いします。