Redux for iOS App

# はじめに
業務でReduxを採用した案件に携わってだいぶ時間も経ったため、アウトプットとして書いていきます。
本記事では、iOSアプリ開発におけるReduxアーキテクチャの開発の基本について解説します。

## Reduxとは
Reduxとは、そもそもReact.jsでstate(状態)を管理をするためのフレームワークです。
iOSではReduxを実現するために使用されるライブラリとして、[ReSwift](https://github.com/ReSwift/ReSwift)があります。今回はReSwiftを題材として説明を進めます。

## Reduxの利点
個人的に上げる利点は以下の2つです。
- 一貫性のある状態管理
- 単一方向のデータフロー

アプリケーションの状態を一つのStoreで管理するため、一貫性が保たれます。
また、アプリケーション内でのデータの流れが単一方向であるため、データフローがシンプルで理解しやすくなります。
これにより、デバッグやテストが容易になります。

# Reduxの基本概念
Reduxには以下の3つの原則が存在します。
- Single source of truth / 信頼できる唯一の状態
- State is read-only / 状態はイミュータブル
- Changes are made with pure functions / 純粋関数

### Single source of truth / 信頼できる唯一の状態
Redux では各状態のインスタンスがあちこちに分散することなく、アプリケーション全体の状態を単一のオブジェクトツリーで管理します。
この状態のオブジェクトツリー(図 9.2)を State と呼びます。State は関数を所有しないデータのみで表現されるシンプルなオブジェクトで構成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425368/81e3f349-7df5-0dc7-b519-6928426b7e14.png)
[^1]

### State is read-only / 状態はイミュータブル
ReduxではStateを直接変更することはできません。
では、どのようにして State の変更を行えばよいのでしょうか。
State の変更 は Action がディスパッチを介して Reducer のみ実行できるように制約されています。
図 9.3 では Reducer による State の変更手続きを示しています。
Reducer は現在の State とディスパッチされた Action の2つを入力に受け、新しい State を 出力する関数です。
現在の State はイミュータブルであるため値の変更を行わず、現在の State のコピーを作ります。
Reducer に記述されたビジネスロジックの実行結果をコピーした State に適用し、新たな State として出力します。このように Reducer では State の変更を実施しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425368/2771b5a3-95d1-7767-7add-03f1fd6c3d1b.png)
[^2]

### Changes are made with pure functions / 純粋関数
新たな State の作成を担う Reducer は 関数として表現します。
ここでいう関数とは、オブジェクト指向なクラスやメソッドによる記述ではなく、Reducer 自身が関数(Reducer 関数) として記述されます。
さらに、その関数は **純粋関数**であることが求められます。
つまり、Reducer関数の中で新たなStateを作って返すという処理になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425368/862c9d43-1525-3243-8572-031603df62b3.png)
[^3]

#### 純粋関数とは
純粋関数は以下の特徴を持った関数です。
- 与えられた要素や関数外の要素を変化させない
- 戻り値以外の出力を行わない
- 取り扱うすべての要素が引数として宣言されている
- 入力に対して出力が常に一意である(同じ入力には常に同じ出力を返す) 

以下のコードが簡単な純粋関数の例です。
```Swift
// 純粋関数の例：引数として与えられた整数を2倍にして返す関数
func doubleValue(_ value: Int) -> Int {
    return value * 2
}

// 使用例
let number = 5
let doubledNumber = doubleValue(number)
print(doubledNumber) // 出力: 10
```

## Reduxの概念図
ここまでの話をまとめると以下の図のようになります。
1. ユーザーの入力をもとにActionを作成
1. ActionをStoreへDispatch（送信）する
1. ActionをもとにReducerがStateを更新
1. StateをもとにUIを更新
![redux_date_flow.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425368/bde113a1-ef95-9682-d2c1-3a43a5177e18.gif)
[^4]

# Stateの定義
アプリケーションの状態を表すStateを定義します。Stateはイミュータブルなデータ構造として表現され、アプリケーション全体の状態を保持します。

この例では、AppStateという構造体を定義しています。
AppStateはStateTypeプロトコルに準拠しており、counterというプロパティを持っています。
ReSwiftの機能であるStateTypeを使うと、アプリ側でReduxのStateとして扱うことができます。
```Swift
struct AppState {
    var count: Int = 0
}
```

# Actionの定義
この例では、IncrementActionとDecrementActionという2つのActionが定義されています。これらのアクションは、カウンターの値を増やすか減らすために使用されます。
```swift
extension AppState {
    struct IncrementAction: Action {}
    struct DecrementAction: Action {}
}
```

# Reducerの定義
この例では、appReducerというレデューサー関数が定義されています。
関数は2つのパラメーターを受け取ります：ActionとStateです。
Stateは現在のアプリケーションのステートを表し、Actionは発行されたアクションです。

Reducerの定義内では、switch文を使用してアクションの種類を判別し、それに応じたステートの変更を行います。
この例では、IncrementActionが発行された場合はcounterを1増やし、DecrementActionが発行された場合はcounterを1減らします。

最後に、変更されたステートを返しています。

このようにReducerを作成することで、Actionに応じてStateが変化し、アプリケーションの状態が管理されます。
```swift
func appReducer(action: Action, state: AppState?) -> AppState {
    var state = state ?? AppState()

    switch action {
    case is AppState.IncrementAction:
        state.count += 1
    case is AppState.DecrementAction:
        state.count -= 1
    default: break
    }

    return state
}
```

# Storeの定義
Storeはシングルトンパターンで実装します。
シングルトンで実装することで、アプリ内部で唯一のStoreであることが保証されます。
このシングルトンで実装されたStoreを使ってStateをViewで参照したり、UIイベントが発生した時にActionをdispatchします。
```swift
final class AppStore {
    static let shared = AppStore()

    let store: Store<AppState>

    private init() {
        store = Store<AppState>(reducer: appReducer, state: nil)
    }
}
```

# Viewでの使い方
こちらがViewへの適応例です。
incrementButtonとdecrementButtonの2つを画面に配置します。
これらのボタンを押すと、Stateで保持しているcountプロパティを増減させることができます。
```swift
incrementButton.rx.tap
    .subscribe(onNext: { [weak self] _ in
            guard let self = self else { return }
            store.dispatch(AppState.IncrementAction())
            self.counterLabel.text = "\(store.state.count)"
        })
        .disposed(by: disposeBag)

decrementButton.rx.tap
    .subscribe(onNext: { [weak self] _ in
            guard let self = self else { return }
            store.dispatch(AppState.DecrementAction())
            self.counterLabel.text = "\(store.state.count)"
        })
        .disposed(by: disposeBag)
```
例えば、incrementButtonが押されたときは以下のような挙動をします。
- incrementButtonが押されると、IncrementActionを発行
- IncrementActionが発行されると、Reducerが呼ばれてReducerが新たなStateを生成
- Reducerが呼ばれることで、Stateのcountプロパティの値が1増える

# まとめ
本記事では、iOSアプリ開発におけるReSwiftを利用したReduxアーキテクチャの開発の基本について解説しました。

[GitHub](https://github.com/KaitoKudou/reswift-sample)にもサンプルコードを置いているので、興味のある方はぜひ覗いてみてください！

他のアーキテクチャと比較しても学習コストが大きいReduxですが、一貫性のある状態管理と単一方向のデータフローを実現できるかなり強力なアーキテクチャなので、Reduxの導入を考えている方に参考になれば幸いです。

[^1]: iOS設計パターン入門 p168
[^2]: iOS設計パターン入門 p169
[^3]: iOS設計パターン入門 p170
[^4]: https://redux.js.org/tutorials/fundamentals/part-1-overview