ReSwiftでReduxアーキテクチャのサンプルを作る

## 概要
Reduxアーキテクチャでカウンターアプリを作ります。
Reduxを体験することが目的です。
動作は下のような感じです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/88c422d9-a58a-3246-f28f-6300c5a6fd17.gif" width="300" />

全体のソースコードはこちらです。

https://github.com/h-taro/ReduxPlayground

## Reduxアーキテクチャの登場人物
Reduxの登場人物を紹介します。

### View
画面表示・ユーザーイベントをハンドリングします。

### State
アプリケーションの状態を管理します。

### Action
Stateをどのように変更するか表現します。

### Reducer
StateとActionから新たなStateを生み出します。

### Store
StateとReducerを内包します。
Actionのディスパッチを担当します。

### Reduxの流れ
主に以下の流れでアプリケーションの状態を更新します。
1. Viewでイベントが発生する
2. イベントをトリガーに`Store`が`Action`をディスパッチする
3. `Reducer`がディスパッチされた`Action`と既存の`State`から新しい`State`を生成する
4. `State`を監視している`View`が更新される

## カウンターアプリを作る
Reduxを使った実装を体験してみます。

### プロジェクトを作成する
Xcodeで新規プロジェクトを作ります。
その後、Swift Package MangerでReSwiftをインストールします。

### `State`を実装する
カウンターアプリなので数字を状態管理します。

```swift:State/CounterState.swift
struct CounterState {
    var number: Int = 0
}
```

### `Action`を実装する
数字を一つ増やす`Action`と数字を一つ減らす`Action`を準備します。

```swift:State/CounterState.swift
extension CounterState {
    enum Action: ReSwift.Action {
        case increment
        case decrement
    }
}
```

### `Reducer`を実装する
`Action`と既存の`State`から新しい`State`を生成します。

```swift:CounterState.swift
extension CounterState {
    ...
    static func reducer(action: ReSwift.Action, state: CounterState?) -> CounterState {
        var state = state ?? CounterState()
        
        guard let action = action as? CounterState.Action else { return state }
        switch action {
        case .increment:
            state.number += 1
        case .decrement:
            state.number -= 1
        }
        
        return state
    }
}

```

### `State`を束ねる`AppState`を実装する
`State`のルートに位置する`State`を実装します。
`AppState`と名づけることが多いです。

```swift:AppState.swift
struct AppState {
    var counterState = CounterState()
}
```

### `AppState`と対応する`Reducer`を実装する
`AppState`に対応する`Reducer`を実装します。
発生した`Action`を下位の`Reducer`に渡します。
今回の場合は `appReducer -> CounterState.reducer`となります。

```swift:AppState.swift
func appReducer(action: ReSwift.Action, state: AppState?) -> AppState {
    var state = state ?? AppState()
    state.counterState = CounterState.reducer(action: action, state: state.counterState)
    return state
}
```

### `Store`を実装する
`Store`は以下の特徴を持ちます。
1. アプリケーションに一つ
2. `State`, `Reducer`を保持する
3. `Action`のディスパッチを担当する

`AppDelegate`で生成しましょう。

```swift:AppDelegate.swift
import UIKit
import ReSwift

let appStore = ReSwift.Store<AppState>(reducer: appReducer, state: nil)
...
```

### ボタンタップ時に`Action`をディスパッチする
ボタンタップ時にディスパッチして、新しい`State`が生成されるようにします。
また、`Store`がViewのイベントを監視できるようにします。

```swift:ViewController.swift
import UIKit
import ReSwift

class ViewController: UIViewController, StoreSubscriber {
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        appStore.subscribe(self)
    }
    
    func newState(state: AppState) {
        label.text = String(state.counterState.number)
    }
    
    @IBAction func tapCountUpButton(_ sender: UIButton) {
        appStore.dispatchFunction(CounterState.Action.increment)
    }
    
    @IBAction func tapCountDownbutton(_ sender: UIButton) {
        appStore.dispatch(CounterState.Action.decrement)
    }
}
```

Reduxのデータフローを実現できました。
1. Viewでイベントが発生する
2. StoreがActionをディスパッチする
3. ReducerがActionを解析して、新しいStateを生成する
4. 新しいStateを元にViewが更新される

ソースコードはこちらです。

https://github.com/h-taro/ReduxPlayground

## 参考リンク

https://peaks.cc/books/iOS_architecture

https://github.com/ReSwift/CounterExample