115
105

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swiftで型安全なFluxアーキテクチャを実現する

Last updated at Posted at 2015-08-03

iOSアプリ開発でもFluxしたい

WebアプリケーションのフロントエンドをReact + Fluxな構成で開発すると、若干コードが増えて面倒にはなりつつも、
ビューの状態やデータの流れが明示的になってとてもわかりやすいなと改めて感じてます。
iOSアプリでもこういうことがやりたいのですが、基本的にFluxは考え方に過ぎないので必要な実装は自分でやらないといけません。

なので作ってみました。
https://github.com/yonekawa/SwiftFlux

使い方

基本的には必要なAction protocolとStore protocolを実装したクラスを作り、DispatcherとEventEmitterを使ってView - Action - Store - View の単方向データフローを作るだけです。詳しくはREADMEをご覧ください。

//: Step 1: Actionを定義
class TodoAction {
    class Create : Action {
        typealias Payload = Todo
        func invoke() {
            let todo = Todo(title: "New ToDo")
            Dispatcher.dispatch(self, result: Result(value: todo))
        }
    }
}

//: Step 2: Storeを定義してdispatchされたいActionにDispatcher.registerする
class TodoStore : Store {
    static let instance = TodoStore()

    enum TodoEvent {
        case Created
    }
    typealias Event = TodoEvent

    var todos = [Todo]()
    var list: Array<Todo> {
        get {
            return todos;
        }
    }

    init() {
        Dispatcher.register(TodoAction.List()) { (result) -> Void in
            switch result {
            case .Success(let box):
                self.todo = box.value
                EventEmitter.emit(self, event: TodoEvent.Created)
            case .Failure(let box):
                break;
            }
        }
    }
}

//: Step 3: ViewController側でEventをlistenする
EventEmitter.listen(TodoStore.instance, event: TodoStore.Event.List) { () -> Void in
    for todo in TodoStore.instance.list {
        plintln(todo.title)
    }
}

//: Step4: ユーザーのインタラクションに応じてActionをinvokeする
TodoAction.List().invoke()

何が嬉しいの?

JavaScriptのように型が無い言語におけるFlux実装では、Actionから何がStoreに流れてくるのかは実装を見ないとわからず、
SwiftFluxではSwiftのジェネリクスやenumを使って、型安全なFluxを実現するように設計されています。
例えば上記のコード例だと、TodoAction.Createアクションが実行されて、Storeに渡ってくる値はtypealiasとジェネリクスによって必ずTodoオブジェクトであることが示されます。
型安全であることにより、Xcodeのコード補間が強力に動作します。Dispatcher.dispatchに渡す値はTodoであることがわかるため、コード補間やコンパイラによるチェックによって迷うことなくコードを書くことができます。

また、よくあるFlux実装ではdispatchされたブロックでActionのTypeによってswitchで処理を分岐することが多いですが、registerするActionを指定するようなインタフェースにすることでブロックに渡ってくる結果が自明になりました。
結果にはantitypical/Resultを使うことで、アクションが成功した時と失敗した時の型をより明確に示すことができます。

実装にあたってはAPIKitの設計を大いに参考にしました。

今後の予定

直近では、Swift2 にすれば Dispatcher もプロトコルベースにしてデフォルト実装を提供すればアプリの性質によって実装を差しかえることもやりやすくなりそうなので対応したいなと思っています。
また、EventEmitterは型を重視してStoreのEventしかlistenできなくしてあるので、Swift2化したらこれはStoreのデフォルト実装にしたいです。
Dispatcher.registerに対してActionのインスタンスが必要になるのが微妙だと思っていて、ここはなんとかしたいところです。

とりあえず自分で使いたいがために作ったので、まずはこれをベースにアプリを書いてみて徐々に改善していきたいと思ってます。

115
105
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
115
105

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?