Redux+Rxを活用したiOSアプリアーキテクチャ


この記事は[FOLIO Advent Calendar](https://qiita.com/advent-calendar/2017/folio-sec)5日目の記事です。

前日は @odakyoka さんの[1ヶ月で1万人のフォロワーを増やしたデザインマーケティング戦略議事録](http://odakyoka.hatenablog.jp/entry/2017/12/04/110429)でした。

[株式会社FOLIO](https://folio-sec.com/)というテーマ投資型オンライン証券サービスを提供している会社で、iOSエンジニアをしています、 @susieyy です。 

絶賛開発中のアプリのアーキテクチャにReduxを採用しました。iOSアプリでどのようにReduxを活用しているのかをご紹介いたします。

# Reduxとは

iOSエンジニアには、[Redux](https://github.com/reactjs/redux)に馴染みのない方も多いのではと思うので、まずは[Redux](https://github.com/reactjs/redux)の解説から。

[Redux](https://github.com/reactjs/redux)は3つの原則(Redux Three Principles)を遵守するよう設計されています。


- Single source of truth
- State is read-only
- Mutations are written as pure functions


![5.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/0fe0b5b7-18f6-30e1-aefc-bd83dbcfb6d8.jpeg "5.jpeg")

アプリ全体で１つの状態を保持するのは、iOSだとデータをすべてSingletonに保持するイメージです。画面毎にデータのスコープを区切ることが多いので、あまりないのではないでしょうか。

![6.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/3fe20ed2-6baa-99b7-94ad-a99e54b6c77c.jpeg "6.jpeg")

状態はイミュータブル（不変）とします。Swiftの`let`や`struct`とも相性がよさそうです。

![7.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/57b91a9b-1fce-8e53-64b1-2e60734e982f.jpeg "7.jpeg")

既存の状態を変更せずに、既存状態と、新しい状態への指示及びインプットデータ（Action）を用いて、変更ロジック（Reducer）を経て新しい状態を作成します。よって、アプリが参照する状態（インスタンス）はイミュータブルですが、Action⇒Reducerを実行するして、新しい状態（インスタンス）を参照することで、アプリからの状態参照においては非破壊・変更不可を実現しています。
 

## 単方向のデータフロー / Unidirectional Data Flow

![9.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/f849c411-41e7-34bc-7490-650bafaaddb9.jpeg "9.jpeg")


上述の３原則を踏まえ、処理の流れ（状態の変化の作用）は上記図のように、必ず１方向に定まるように準拠します。

MVC、MVVM、やレイヤードアーキテクチャ(e.g. クリーンアーキテクチャ)では、各レイヤー間のやりとりは双方向となりますが、Reduxでは単方向に制限することで、処理の流れ（プログラムの記述）をシンプルにし、作用を把握しやすく、動作時の内部挙動をわかりやすくします。また、この単一の処理の流れは、マルチスレッドなアプリにおいても、処理を単一スレッドで逐次実行とすることで、マルチスレッドにおける複雑性も排除しています。


## POINTをまとめると以下のようになります

**予測可能**な形でコードを**宣言的**に構造化すること
- **予測可能**
  - 状態を一元的に管理
  - 状態変化はシーケンシャル
  - 副作用との分離
- **宣言的**
  - 状態変化の起因が明示的 （ ActionをDispatch ）
  - 状態変化は純粋関数


## Reduxをなにで実装するか

iOS開発で[Redux](https://github.com/reactjs/redux)を活用にあたり、先駆者の方が実装くださったいくつかのオープンソースライブラリが既に存在します。

- 本家[Redux](https://github.com/reactjs/redux)のステップ数は`318`と非常に少ない
- 実装を参考に自分でポーティングすることも可能な分量
- ライブラリもいくつか実装されているので検討してみる


## Redux系ライブラリ

- [ReSwift](https://github.com/ReSwift/ReSwift) ★4,247
- [ReactiveReSwift](https://github.com/ReSwift/ReactiveReSwift) ★71
- [KATANA](https://github.com/BendingSpoons/katana-swift) ★ 1,602
- [ReduxKit](https://github.com/ReduxKit/ReduxKit) ★583
- [Reactor](https://github.com/ReactorSwift/Reactor) ★129


## Flux系ライブラリ

- [Dispatch](https://github.com/alexdrone/Dispatch) ★239
- [SwiftFlux](https://github.com/yonekawa/SwiftFlux) ★212
- [FluxWithRxSwiftSample](https://github.com/dekatotoro/FluxWithRxSwiftSample) ★109
- [FluxxKit](https://github.com/keitaoouchi/FluxxKit) ★33


## 検討結果

独自実装も可能そうな規模ですが、今回は以下の観点から[ReSwift](https://github.com/ReSwift/ReSwift)を採用しました

- [ReSwift](https://github.com/ReSwift/ReSwift)が一番有名
- 関数やクロージャーを中心に設計されており、実装がとても綺麗
- リリース頻度もほどよく、ISSUEも活発

# ReSwiftとは

ReSwiftの実装と利用例を以下の観点で見てみます。


- State
- Action
- Reducer
- Store
- Dispatch
- ActionCreator

## State

状態（データ）を表現するStateです。

- **Struct**の木構造
- 各**Struct**は**ReSwift.StateType**プロトコルに準拠する
- 慣例的に一番上層の状態を表す**Struct**を**AppState**とする

![17.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/0a60d288-8de0-319a-e975-a5ea4f07b763.jpeg "17.jpeg")

### ReSwiftの実装

**ReSwift.StateType**プロトコルは準拠すべき制約は何もありません。ReSwiftでは**ReSwift.StateType**に準拠したインスタンスをStateとして扱うための目印として利用します。

```swift
protocol StateType { }
```

### e.g. 利用例

シンプルなTwitterアプリを想定しています。以下ようにStructが木構造になっています。慣例的にAppState配下のStateはアプリの各画面毎のStateを担うことが多いです。**ReSwift.StateType**に準拠しない状態（データ）も保持できます。ただし、後述するReducerは**ReSwift.StateType**単位で処理を行うので、どの粒度の状態まで**ReSwift.StateType**に準拠するか判断が必要です。

```swift
struct AppState: ReSwift.StateType {
    var timelineState = TimelineState()
    var userProfileState = UserProfileState()
}

struct TimelineState: ReSwift.StateType {
  var tweets: [Tweet]
  var response: [Tweet]
}

struct UserProfileState: ReSwift.StateType {
  var profile: [Profile]
  var profileImage: URL
  var response: [Tweet]
}
```

## Action


状態（データ）の変更を指示するために表現するActionです。

- **ReSwift.Action**プロトコルに準拠していれば、**Struct**でも、**Enum**でもよい
- 処理を有しないただのデータ
- **Reducer**でどういう処理をしたいかの種類とそのインプットデータになる

![21.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/1c7b81f4-91a3-2eb3-9929-d9fb30776e82.jpeg "21.jpeg")


### ReSwiftの実装

Stateと同様に、**ReSwift.Action**プロトコルは準拠すべき制約は何もありません。ReSwiftでは**ReSwift.Actione**に準拠したインスタンスをAactionとして扱うための目印として利用します。

```swift
protocol Action { }
```

### e.g. 利用例

ここでは、ActionをStructではなく、Enumで表現しました。ReSwiftではActionの種別を、Structの場合はその型の違いにより、Enumの場合は、Enumのその型とcaseにより、区別します。区別とはどの指示かを表します。指示に付随するインプットデータはStructではプロパティが、EnumではAssociated Valueが担います。

```swift
extension TimelineState {
  enum Action: ReSwift.Action {
    case requestSuccess(response: [Tweet])
    case requestState(fetching: Bool)
    case requestError(error: Error)
  }
}
```


## Reducer

状態（データ）の変更を指示するためのActionを受けて、Stateを変更するReducerです。Actionの指示の種類（型）により、実行されるReducerが決定します。木構造のStateの各State毎にReducerが紐づくようになります。

Reducerは、現在のStateと、変更指示であるActionを引数にとり、新しいStateを返す関数です。またReducerは純粋関数であることが求められるます。純粋関数とは、関数の処理中に副作用なく任意の引数に対して、常に一意な値を返す関数のことをいいます。

ActionはすべてのRedercerに渡されます（Broadcast）。各々のReducerは渡されたActionは自身が処理すべきActionなのかを判断（型マッチ）の上、必要であれば、Actionの指示に従い紐いたStateを変更します。

- **Redux**では状態変更できるのは**Reducer**だけという制約
- **Reducer**は `(state, action) => state` を満たす状態を持たないふつうの関数（純粋関数）でなければならない


![25.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/5d2b300c-b641-5629-6827-f4f4ed23fe5f.jpeg "25.jpeg")



### ReSwiftの実装


typealiasでクロージャーを定義しているだけなところが美しいですね。

```swift
typealias Reducer<ReducerStateType> =
    (action: Action, state: ReducerStateType?) -> ReducerStateType
```

### e.g. 利用例

シンプルなTwitterアプリのタイムライン画面用のStateにおけるRedcuerです。

- もし自身に紐づくStateがまだインスタンスが作成されていない場合は生成
- Actionが自身のReducerで処理すべきかEnumの型を判断
- 処理すべきActionの場合はEnumのパターンマッチを活用して、漏れなく処理を記述
- 各Enumのケース毎にStateを変更するロジックを記述
- 最後に変更後のStateを返す


```swift
extension TimelineState {
    public static func reducer(action: ReSwift.Action, state: TimelineState?) -> TimelineState {
        var state = state ?? TimelineState()

        guard let action = action as? TimelineState.Action else { return state }
        switch action {
        case let .requestState(fetching):
            state.fetching = fetching
            state.error = nil
        case let .requestSuccess(response):
            state.fetching = false
            state.response = response
            state.dataSourceElements = DataSourceElements(response.map({ DiffableWrap($0) }))
        case let .requestError(error):
            state.fetching = false
            state.error = error
        }
        return state
    }
}
```

## Reducer / Initialization


- 慣例的に一番上層の状態を表す**Struct**を**appReduce**とする
- **appReduce**を起点に、下位の**Reducer**に**Action**をブロードキャストする


```swift
func appReduce(action: ReSwift.Action, state: AppState?) -> AppState {
    var state = state ?? AppState()
    state.timelineState = TimelineState.reducer(
                                        action: action,
                                        state: state.timelineState)

    state.userProfile = UserProfileState.reducer(
                                        action: action,
                                        state: state.userProfile)
    return state
}

var appStore = ReSwift.Store<AppState>(
                              reducer: appReduce,
                              state: nil,
                              middleware: [])
```


## Store

状態（データ）を保持するのStoreです。基本的にアプリ内で１つのStoreとなります。Storeはシングルトンでインスタンもアプリ内で一意です。

Reducerが新しい状態を作成したら、既存の状態と新しい状態を入れ替えます。入れ替えによりイミュータブルである状態は、既存の状態を非破壊で扱えるようになります。

また、ActionをReducerに伝えるための、Dispatch、Stateの状態が新しく作られたことをViewに伝えるSubscribeの機能も有しています。


- **State**と**Reducer**を保持するシングルトン
- **Action**のディスパッチメソッドを有する
- **State**のサブスクライブメソッドを有する


![31.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/0d84705d-1b6a-f424-3165-4e536658c283.jpeg "31.jpeg")


### ReSwiftの実装


```swift
open class Store<State: ReSwift.StateType>: ReSwift.StoreType {
    var state: State! { get }
    private var reducer: Reducer<State>

    open func dispatch(_ action: Action) {
        ...
    }

    open func subscribe<S: StoreSubscriber>(_ subscriber: S) {
        ...
    }

    open func unsubscribe(_ subscriber: AnyStoreSubscriber) {
        ...
    }
    ...
}
```

## Dispatch

ViewからActionをReducerに伝えたい場合に、StoreのDispatchメソッドを利用します。

- **View**側で**Action**のインスタンスを作成し、**Store**の**Dispatch**メソッドをコールする

![34.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/56280a7a-0ab3-b63d-df9f-099f35c68ea2.jpeg "34.jpeg")



### e.g. 利用例

Actionのインスタンを生成し、シングルトンであるappStoreのdispatchメソッドにアクションのインスタンを渡します。

appStoreはシングルトンですが、必要であればDIなどで、参照をVCにインジェクションするようにして参照を局所化してもよいでしょう。dispatch可能なViewが限定されます。

```swift
let action = TimelineState.Action.requestState(fetching: true)
appStore.dispatch(action)
```



## ActionCreator

Dispatch可能な関数でActionを返します。

- **ActionCreator**関数内では**State**にアクセス可能で、**State**を加工して**Action**を作成する用途に利用できる
- **ActionCreator**関数内では**Store**にアクセス可能で、**Dispatch**メソッドをコールすることもできる


![37.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/6814307a-ac1f-f7a3-5755-9c3e70a3e8af.jpeg "37.jpeg")


# ReSwift Rx活用編

ReSwiftをRxSwiftと共に活用することで、ReSwiftの課題点を解決できます。以下２点の観点から、どのような課題を、どのようにアプローチしているのか見てみましょう。


- 副作用（非同期通信）
- ViewDataBinding

## 副作用（非同期通信）/ Asynchronous Operations


**ReSwift**のREADMEによる非同期通信の例です。**ActionCreator**内で副作用（非同期通信）を行っています。以下のような課題点があるように感じます。

- **ActionCreator**関数が純粋関数ではなくなる
- 関数内に副作用（非同期通信）があるとテストがしにくい
- 美しくない(個人の感想です)
- とはいえ、**Reducer**は純粋関数で副作用を許容しないので、**Reducer**にも記述できない

```swift
func fetchGitHubRepositories(state: State, store: Store<State>) -> Action? {
    guard case let .LoggedIn(configuration) = state.authenticationState.loggedInState  else { return nil }

    Octokit(configuration).repositories { response in
        dispatch_async(dispatch_get_main_queue()) {
            store.dispatch(SetRepostories(repositories: response))
        }
    }
    return nil
}
```



## Redux(JS)では副作用(非同期処理)をどのように扱っているか

Redux(JS)ではMiddlewareで副作用(非同期処理)を扱います。下の図のActionがDispatchされてから、Reducerで処理が実施されるまでの間に、MiddlewareというActionに作用するロジック層を設けることができます。


![44.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/b0d784b9-070d-1a47-d594-a67f93bfaa18.jpeg "44.jpeg")


## ReSwiftのMiddleware

ReSwiftでは、Middlewareは以下のように定義されます。ちょっと複雑なtypealiasですね。

```swift
public typealias DispatchFunction = (Action) -> Void
public typealias Middleware<State> = (
                                      @escaping DispatchFunction,
                                      @escaping () -> State?
          ) -> (@escaping DispatchFunction) -> DispatchFunction
```

## e.g. 利用例

一番シンプルな利用例である、DispatchされたActionをログに出力する例です。基本的に現在のStateとDispatchされたActionが引数として渡されて、その値を活用しつつ、必要であれば、Actionを変更したり、ログ出力などの副作用を行います。Middlewareも関すですが、純粋関数でなくてもよいです。

```swift
let loggingMiddleware: ReSwift.Middleware<AppState> = { dispatch, getState in
    return { next in
        return { action in
            logger.info("🔥 [Action] \(action)")
            return next(action)
        }
    }

var appStore = Store<AppState>(
      reducer: appReduce,
      state: nil,
      middleware: [loggingMiddleware]
    )
```


## Redux(JS)の非同期用Middlewareたち

Redux(JS)で副作用(非同期処理)を扱うためのライブラリがいくつかありますが、以下が有名でしょうか。

- redux-thunk
- redux-sage
- redux-promise

## Redux thunkのソースコード（全量）

ここでは、redux-thunkについてのソースコードを見てみます。以下がライブラリのソースコード全量ですが、大変コンパクトです。行っていることはActionはデータ型だけではなくて関数型も許容し、Actionが関数型の場合は、関数を実行した上、その戻り値はActionとして次のMiddlewareに渡しています。

ここで実行する関数を副作用（非同期通信）を許容する（純粋関数ではない）ことで、ActionCreatorとReducerは純粋関数ですが、副作用（非同期通信）の処理をMiddlewareにのみ局所化することができます。

```js
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;
```

ふむふむ🤔

## RxSwift活用して非同期処理をMiddlewareに押し込む

redux-thunkを参考に、SingleActionというRxSwiftのSingleを保持するActionを定義し、その型をMiddlewareで検知した場合は、subscribeすることでRxSwiftを活用した非同期処理をMiddlewareで行うことを実現しています。


```swift
struct SingleAction: ReSwift.Action {
    public let single: Single<ReSwift.Action>
    public let disposeBag: DisposeBag
}

let rxThunkMiddleware: ReSwift.Middleware<AppState> = { dispatch, getState in
    return { next in
        return { action in
            if let action = action as? SingleAction {
                action.single
                    .observeOn(MainScheduler.instance)
                    .subscribe(onSuccess: { next($0) })
                    .disposed(by: action.disposeBag)
            } else {
                return next(action)
            }
        }
    }
}
```

## e.g. 利用例

ActionCreatorでは、RxSwiftを利用して通信を宣言的に記述します。ポイントは、mapとcatchErrorで通信結果を成功時も、失敗時も共にActionに変換しています。これにより、Middlewareでsubscribeされた通信結果はActionとなり、Reducerへと伝えられます。

```swift
func requstAsyncCreator(maxID: String) -> Store<AppState>.ActionCreator {
    return { (state: AppState, store: Store<AppState>) in
        if state.timelineState.fetching { return nil }

        let single = TwitterManager.shared.timeline(maxID: maxID)
                    .map {
                        return Timeline.Action.requestSuccess(response: $0)
                    }
                    .catchError {
                        let action = Timeline.Action.requestError(error: $0)
                        return Single<ReSwift.Action>.just(action)
                    }
        return SingleAction(single: single, disposeBag: state.timelineState.requestDisposeBag)
    }
}
```

 
## Testability


Reduxの世界はイミュータブル、純粋関数、副作用のみで構築されています。テストがしにくい副作用部分を局所化すること、関数は純粋関数であること、データはイミュータブルであることでテストを容易にしています。

- イミュータブルは不変
  - **Action**（インプットパラメーター）、**State**（データ）
- 純粋関数はテストが容易
  - 任意のインプットに対してアウトプットが一意に決まる
  - **ActionCreator**と**Reducer**
- 副作用はテストが複雑
  - **Middleware**の部分はテスト時にスタブに差し替え


テスト容易性のために、ビジネスロジックには、依存性のあるコンポーネントらの参照をDI(Dependency Injection)でテスト時に差し替え可能とする方法がよく用いられますが、このアーキテクチャでは下図のようにテスト時に副作用部分であるMiddlewareのみStubに変更することで、副作用との依存性の切り離しを可能としています。またDI不要の設計となっています。💪

![57.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/87a92141-b28e-4ee2-faa8-8f8c4744ddde.jpeg "57.jpeg")


## ViewDataBinding

ReSwift#subscribe

```swift
override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    store.subscribe(self) { subcription in
        subcription.select { state in state.repositories }
    }
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    store.unsubscribe(self)
}

func newState(state: Response<[Repository]>?) {
    if case let .Success(repositories) = state {
        dataSource?.array = repositories
        tableView.reloadData()
    }
}
```

ReSwift.StoreをRxSwift.Variableに変換

```swift
let rxReduxStore = RxReduxStore<AppState>(store: appStore)

public class RxReduxStore<AppStateType>: StoreSubscriber where AppStateType: StateType {
    public lazy var stateObservable: Observable<AppStateType> = {
        return self.stateVariable.asObservable().observeOn(MainScheduler.instance)
                                                .shareReplayLatestWhileConnected()
    }()
    public var state: AppStateType { return stateVariable.value }
    private let stateVariable: Variable<AppStateType>
    private let store: Store<AppStateType>

    public init(store: Store<AppStateType>) {
        self.store = store
        self.stateVariable = Variable(store.state)
        self.store.subscribe(self)
    }

    deinit {
        self.store.unsubscribe(self)
    }

    public func newState(state: AppStateType) {
        self.stateVariable.value = state
    }

    public func dispatch(_ action: Action) {
        store.dispatch(action)
    }

    public func dispatch(_ actionCreatorProvider: @escaping (AppStateType, ReSwift.Store<AppStateType>) -> Action?) {
        store.dispatch(actionCreatorProvider)
    }
}
```

## e.g. 利用例



```swift
class TimeLineViewController: UIViewController {
    fileprivate let disposeBag = DisposeBag()
    fileprivate let rxReduxStore: RxReduxStore<AppState>

    init(_ rxReduxStore: RxReduxStore<AppState>) {
        self.rxReduxStore = rxReduxStore
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        rxReduxStore.stateObservable
            .map { $0.timelineState.dataSourceElements }
            .bind(to: adapter.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)

        rxReduxStore.stateObservable
            .map { $0.timelineState.fetching }
            .distinctUntilChanged()
            .bind(to: loadingView.rx.fetching)
            .disposed(by: disposeBag)
    }
}
```

# Viewの差分更新

![66.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/b2d5943e-0dba-ec29-98fd-06e6f0437459.jpeg "66.jpeg")


## ReactではVirtual DOM

![68.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/c15aa183-ec64-bf4b-137f-d40e41f457fe.jpeg "68.jpeg")


Viewコンポーネントで差分更新が可能

![69.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/d3ee4cb8-69fa-d024-f0c0-924e5f0fccd8.jpeg "69.jpeg")


iOSでは??

## IGListKit

![71.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/95bc6ae8-a8ee-77d5-2a3d-26ac05f756b0.jpeg "71.jpeg")




![72.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/704dfe51-5f4d-9017-4a02-e91716d48cfb.jpeg "72.jpeg")


![73.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/5889dcef-44cb-2d4d-8542-7465d5e1b0bd.jpeg "73.jpeg")


![74.jpeg](https://qiita-image-store.s3.amazonaws.com/0/4943/f49303d0-9a32-a4cc-3553-731f559f2ea1.jpeg "74.jpeg")


- **UICollectionView**をベースにできている
- データはいろんな型の要素を含む１次元配列
- データの要素の型でコンポーネントを分岐する
- データの要素は比較的可能なために**IGListKit**のプロトコルに準拠する(Equitableみたいなもの)
- **ObjC**なところが悲しい😢
     - プロトコル準拠のためにNSObjectを継承が必要

```swift
final class TimeLineViewController: UIViewController {
    fileprivate let dataSource = DataSource()
    fileprivate lazy var adapter: ListAdapter = ListAdapter(updater: ListAdapterUpdater(), viewController: self)
    fileprivate let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        adapter.collectionView = collectionView
        adapter.rx
            .setDataSource(dataSource)
            .disposed(by: disposeBag)

        rxReduxStore.stateObservable
            .map { $0.timelineState.dataSourceElements }
            .bind(to: adapter.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
    }
}

extension TimeLineViewController {
    fileprivate final class DataSource: AdapterDataSource {
        override func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
            switch object {
            case let o as DiffableWrap<Tweet>: return TweetsSectionController(o)
            case let o as DiffableWrap<[RecommendUser]>: return RecommendUsersSectionController(o)
            }
        }
    }
}
```

# Conclusion

iOSでReduxアーキテクチャ良いよ👍