iOS meets Flux

# iOS with Flux

- Fluxとは
    - SwiftFlux
- Redux
    - ReSwift
- with RxExtention
    - Delta

----

# Flux

![flux-diagram](https://github.com/facebook/flux/raw/master/docs/img/flux-diagram-white-background.png)

----

# Fluxとは

- Application Architecture のひとつ
    -  2014/5/6 にF8でトーク
    -  https://facebook.github.io/flux/docs/overview.html
    -  yet another MVC
    -  一方向のデータフローが特徴

---

# MVCのおさらい

- Model ... データ構造・ビジネスロジック(笑)
- View ... Model(データ)を表示
- Controller ... Viewのイベントを受け取りModelへメッセージを発行


---

# wikipediaより


![wiki_mvc](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/MVC-Process.svg/200px-MVC-Process.svg.png)

----

# あれっ

----

# 一方向じゃね

----

# Image search wth `MVC`

![Screen Shot 2016-06-28 at 11.26.22 PM.png](https://qiita-image-store.s3.amazonaws.com/0/15815/e0fa32ae-948f-b4b7-636c-6da8e4d46880.png)

----

# MVCの問題点

- シンプルすぎてそれだけでは足りずバリエーションが多い
    - Controller -> View へデータを流しがち
        - サーバサイドMVCの影響
        - アニメーションなどViewが細かい状態をもつとき

----

# MVCの問題点

- 依存関係が複雑に
    - ViewやModelがどこで変更されたのがわかりづらく

![](https://cdn.infoq.com/statics_s2_20160622-0236/resource/news/2014/05/facebook-mvc-flux/ja/resources/flux-react-mvc.png)

----

# Flux

- ルールを決めないと気がついたら崩壊しがちなMVCをベストプラクティス的にパターン化したもの
- 基本はObserverパターン

![flux](https://facebook.github.io/flux/img/flux-simple-f8-diagram-1300w.png)

----

# Structure and Data Flow
 
![flux](https://facebook.github.io/flux/img/flux-simple-f8-diagram-1300w.png)

- Action
- Dispatcher
- Store
- View

----

# A Single Dispatcher

- データフローの中心
- Storeを更新するメッセージ(Action)をここに向けて発行する
- Dispatcherが実際にActionを実行
- 依存関係解決の仕組みもある: Dispatcher.waitFor(dispatherId, () => ())

----

# Store

- アプリの状態とロジック
- Modelと似ているが、複数のオブジェクトの状態を扱う
    - 単にコレクションを扱うということではない
- インタフェースとしてはシングルトン

----

# Views and Controller-Views

- Storeのstateやデータを画面に表示
- front endではReactを使う
    - StoreからまるっとデータをとってきてViewを構築すれば、差分を見つけて描画する
- iOSではここを少し工夫しないといけないかも(後述)

----

# Actions and Action Creators

- Dispatcherが公開しているメッセージ＋ペイロード
        - アプリにとって意味のあるイベントという感じ
            - Commandパターンみたいなもの
- Actionを作るためにActionCreatorというヘルパーメソッドを作ることもある
    - ViewのUIイベントをActionCreatorを使ってActionに変換して、Dispatcherに投げる

----

# iOS開発

- 手持ちの武器
    - UIKit
        - ViewController + View 
        - Delegate pattern
        - DataSource
    - NSNotificationCenter
    - Key-value observing

----

# iOS開発あるある

- ViewControllerに直接データ保存して、後からViewController間でデータを共有したくなって辛い
- Viewから直接データ更新
- NSNotificationCenterのキーが増えてきて辛い
- 誰が発行したイベントかわからない

----

# Let's flux in swift

- [SwiftFlux](https://github.com/yonekawa/SwiftFlux)
- protocol やベースクラスで骨組みを提供

----

# Action

```swift
public protocol Action {
    typealias Payload
    typealias Error: ErrorType = NSError
    func invoke(dispatcher: Dispatcher)
}
```

----

# Dispatcher

```swift

public protocol Dispatcher {
    func dispatch<T: Action>(action: T, result: Result<T.Payload, T.Error>)
    func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> ()) -> DispatchToken
    func unregister(dispatchToken: DispatchToken)
    func waitFor<T: Action>(dispatchTokens: [DispatchToken], type: T.Type, result: Result<T.Payload, T.Error>)
}
```

---- 

# Store

```
extension Store {
....
    public func subscribe(handler: () -> ()) -> StoreListenerToken {
        return eventEmitter.subscribe(self, handler: handler)
    }

    public func unsubscribe(listenerToken: StoreListenerToken) {
        eventEmitter.unsubscribe(self, listenerToken: listenerToken)
    }

    public func unsubscribeAll() {
        eventEmitter.unsubscribe(self)
    }

    public func emitChange()  {
        eventEmitter.emitChange(self)
    }
}
```

---

# Example

```swift
class TodoListViewController: UITableViewController {
    let todoStore = TodoStore()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.todoStore.subscribe { () in
            self.tableView.reloadData()
        }
        ActionCreator.invoke(TodoAction.Fetch())
    }

    @IBAction func createTodo() {
        ActionCreator.invoke(TodoAction.Create(title: "New ToDo"))
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.todoStore.todos.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("TodoCell") as UITableViewCell!
        cell.textLabel!.text = self.todoStore.todos[indexPath.row].title
        return cell
    }

    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        ActionCreator.invoke(TodoAction.Delete(index: indexPath.row))
    }
}
```

---

# SwiftFlux所感

- NSNotificationCenterよりは100倍いい
- Storeを変更するものはそれぞれActionを定義するので、コードが明示的、説明的になる
    - コード量は増える、周りくどいと感じるかも
- ViewはStoreの値から毎回作り直すのが基本なので、パフォーマンスが気になる
    - 差分更新をViewControllerやCustom Viewで頑張るか
    - StoreからのViewへの変更通知は基本変更したかどうかだけだが
        - Storeからのイベントで差分更新できるペイロードを渡すか
        - Viewが状態も持つのやっぱり辛い

----

# Redux

- Fluxの派生版

> Can Redux be considered a Flux implementation?
> Yes, and no.

- Fluxは状態の管理が規定されてなかったので、状態の管理にフォーカスしている

----

# Single source of truth

- アプリケーションの状態をオブジェクトのツリーを持つStoreで表現(Single state tree)
- サーバの状態もstateに変換、落とし込むので、ユニバーサルアプリケーションがつくりやすい
- stateが一つだから、デバッグしやすい、開発しやすい
- Single State treeならUndo/Redoも簡単
    - time travel 
    - やっぱりCommandパターンっぽい

----

# State is read-only

- 状態の変更はActionを発行するしかない
    - ビューやコールバックが状態を直接変更させることはできない
- すべての変更は中央管理され変更は一つずつ順番に行なわれる
- actionはplain objectsなので、保存可能であり、テストしやすい
 
----

### Changes are made with pure functions

- アクションがどのように状態を変更するかを「Reducer」で行う。
- Reducer: (prevState, Action) -> (nextState)
    - Reduce: Array#reduce((prev, current) -> (next), initialValue)
- Stateはimmutable. 現在のstateを変更せずに、新しいstateを作って返すというのがポイント。
- 最初はアプリケーションで一つのReducerから初めて巨大化してきたらReducerを分割

----

![redux](https://github.com/ReSwift/ReSwift/raw/master/Docs/img/reswift_concept.png)

----

# ReSwift

- [ReSwift/ReSwift](https://github.com/ReSwift/ReSwift#example-projects)
- Redux in Swift 
- [ReSwift/CounterExample-Navigation-TimeTravel](https://github.com/ReSwift/CounterExample-Navigation-TimeTravel/tree/master/ReSwiftCounterExample)
- ReSwiftRouter, ReSwiftRecoder でtime travel

----

# Reducer

```
public protocol Reducer : AnyReducer {
    associatedtype ReducerStateType
    public func handleAction(action: Action, state: Self.ReducerStateType?) -> Self.ReducerStateType
}
```

- Stateがassociatedtypeで型パラメータ: ReducerStateType
- ActionとState受け取ってStateを返す関数

----

# AppReducer

```
struct AppReducer: Reducer {

    func handleAction(action: Action, state: AppState?) -> AppState {
        return AppState(
            counter: counterReducer(action, counter: state?.counter),
            navigationState: NavigationReducer.handleAction(action, state: state?.navigationState)
        )
    }
}
```

----

# counterReducer

```
func counterReducer(action: Action, counter: Int?) -> Int {
    var counter = counter ?? 0

    switch action {
    case _ as CounterActionIncrease:
        counter += 1
    case _ as CounterActionDecrease:
        counter -= 1
    default:
        break
    }

    return counter
}
```

----

# Store

- グローバルに一個もつ

```swift:AppDelegate.swift

var mainStore = RecordingMainStore<AppState>(
        reducer: AppReducer(),
        state: nil,
        typeMaps:[counterActionTypeMap, ReSwiftRouter.typeMap],
        recording: "recording.json"
    )

```

----

# StoreSubscriver

```swift:StoreSubscriber
public protocol StoreSubscriber : AnyStoreSubscriber {
    associatedtype StoreSubscriberStateType
    public func newState(state: Self.StoreSubscriberStateType)
}
```


- ViewControllerなどでconfirmする

----

#### ViewController

```
class CounterViewController: UIViewController, StoreSubscriber, Routable {
    @IBOutlet var counterLabel: UILabel!
    override func viewWillAppear(animated: Bool) {
        mainStore.subscribe(self)
    }
    override func viewWillDisappear(animated: Bool) {
        mainStore.unsubscribe(self)
    }
    func newState(state: AppState) {
        counterLabel.text = "\(state.counter)"
    }
    @IBAction func increaseButtonTapped(sender: UIButton) {
        mainStore.dispatch(
            CounterActionIncrease()
        )
    }
   ....
}
```

----

# Delta

- [thoughtbot/Delta](https://github.com/thoughtbot/Delta)
- 状態管理だけにフォーカス
- RxExtensionと組み合わせて使う
    - observerパターンの実装をRxに任せている
        - ReactiveCocoa: MutableProperty

----

# ActionType

```
public protocol ActionType {
    typealias StateValueType
    func reduce(state: StateValueType) -> StateValueType
}
```

- reducer っぽい

----

# Property

```
public protocol StoreType {
    typealias ObservableState: ObservablePropertyType
    var state: ObservableState { get set }
    mutating func dispatch<Action: ActionType where Action.StateValueType == ObservableState.ValueType>(action: Action)
    func dispatch<DynamicAction: DynamicActionType>(action: DynamicAction) -> DynamicAction.ResponseType
}
```

- この ObservableStateをReactiveCocoa.MutablePropertyになるようにstructを実装している
- protocol extensionでうまい事実装してるっぽい


---

# まとめ

- Flux
    - MVC比べて見通し良さげ
    - コード量は増えるかも
    - SwiftFluxなど
- Redux
    - 状態管理をさらに見通しよく
    - グローバル変数で集中管理なので、好き嫌いあるかも
        - 一個だけに決まっているのならむしろ乱立しない分良いかもしれない
- Delta
    - Rxを使ってるならこれがいいかも
    - Rxのユーティリティが使えるのが魅力

-----

# 雑多な感想

- Swiftに型フレンドリーなObserverユーティリティがないのでみんな自前実装している
    - Multiple Delgates欲しい
- ObserverパターンやCommandパターンなど個々の要素は既存パターン
    - クラインアントアプリに向けに特化して、組み合わせにベストプラクティスを提供してくれている
- protocolで実現されてるとcool
    - classかstructかなどを縛らずに枠組みだけを作れる