ReduxとSwiftの組み合わせを利用したUIサンプル事例紹介

## 1. はじめに

8月から9月にかけて技術書典に向けての準備やiOS関連イベントと忙しい感じでしたが、それまでに色々と自分なりにReduxを用いたアーキテクチャーとUIとの組み合わせに関して調査していたことやReactNativeも少々扱っていた経緯もあったので、今回は1つの画面において多くの画面状態が存在するような形のUIサンプルにおいてRedux（ReSwift）を部分的に適用したものを例にとって解説していこうと思います。

__サンプルの全体的な動きの動画：__

+ [1分間のサンプルの動きを解説した動画](https://www.facebook.com/fumiya.sakai.37/videos/2062663777129621/)

__Githubでのサンプルコード：__

+ [Swift4でReduxとUI実装を合わせたサンプル](https://github.com/fumiyasac/ReduxSampleSwift)

※こちらのサンプルはPullRequestやIssue等はお気軽にどうぞ！

本記事でご紹介している内容に関しましては、株式会社Visits Technology様にて開催された__「ROPPONGI.swift 第5回」__の中でも登壇時にサンプルを交えてご紹介しましたので、その際に使用したスライド等も併せて共有致します。

__ROPPONGI.swift 第5回に関する記事：__

+ [第5回ROPPONGI.Swiftを開催しました #roppongswift](https://medium.com/visits-techblog/%E7%AC%AC5%E5%9B%9Eroppongi-swift%E3%82%92%E9%96%8B%E5%82%AC%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F-roppongswift-694f5d6f7ad4)
+ [ROPPONGI.swift #5 にて標準ライブラリだけでスロットゲームを実装した話をしました](https://developer.diverse-inc.com/entry/2018/08/30/172012)

__発表資料：__

+ [Fundamentals of Swift & Redux (ReduxとSwiftの組み合わせ)](https://www.slideshare.net/fumiyasakai37/fundamentals-of-swift-redux-reduxswift)

## 2. 今回の参考資料とサンプル概要について

今回紹介するサンプルについては、 __「チュートリアル → ユーザーの情報入力 → 1画面に複数の画面要素が混在する」__ という形のUI実装を伴ったものになります。
画面遷移以外の画面の要素表示に関する部分をReduxで管理することでそれぞれの状態に合わせた状態を実現するような構成を取っています。またContainerViewを利用した親子関係を持ったViewControllerがある場合でも対処できるような形にしています。Viewの細かな表現部分に関しては若干作り込みが甘い点等はあるかと思いますが、お気づきの点等があればご指摘頂けますと幸いに思います。

#### ★2-1. このサンプルを実装するにあたっての参考資料：

今回のサンプル実装とRedux関連処理部分の構成については、下記の記事等で紹介されていたコードを参考にしました。
またReSwiftのリポジトリの他には下記の様なサンプルコードもありますので、実際に組み込む前の予行演習としてReSwiftやReduxの処理を知る上での手助けになるかと思います。

__ReSwiftのサンプルコード：__

+ [Example Projects (ReSwift)](http://reswift.github.io/ReSwift/master/example-projects.html)

__参考資料：__

+ [Unindirectional Data Flow in Swift](http://ianterrell.com/talks/unidirectional-data-flow-in-swift/#/title-page)
+ [Architecture Thoughts: Migrating Marvel's iOS App to ReSwift](https://medium.com/cocoaacademymag/architecture-thoughts-migrating-marvels-ios-app-to-reswift-ef7f20e84e60)
+ [ReduxのSwift実装「ReSwift」を触ってみる](http://developabout0309.blogspot.com/2018/03/reduxswiftreswift.html)
+ [RxSwiftを投げ出したあなたに贈る、ReduxライクなReSwiftのご提案](https://qiita.com/Hakota/items/bb94e891961b70b64b44)

この記事では触れていませんが、ReSwiftと併せてRxSwiftも一緒を利用した事例については下記の記事も参考になるかと思います。紹介しているサンプルについてはまだ規模が小さいですが、より規模が大きく複雑度が増すようなケースにおいては有効なのではないかと思います（ReSwift+RxSwiftの構成については私もチャレンジしてみたい！）。

+ [Redux+Rxを活用したiOSアプリアーキテクチャ](https://qiita.com/susieyy/items/23d44f28c6a6915c58e2)
+ [ReSwiftのステートをRxSwiftを使って監視する](http://glassonion.hatenablog.com/entry/2017/04/20/195728)

#### ★2-2. 今回のサンプルについて：

__サンプルのデザインや画面構成：__

こちらは初回に起動した際に表示されるチュートリアル画面とユーザーのアンケート情報入力画面になります。例えばチュートリアル画面が終わって再起動した場合にはアンケート情報入力画面から開始できるような考慮を`InitialSettingViewController.swift`の読み込み時に現在のユーザー状態に応じた画面の振り分けをすることで実現しています。

![capture1.jpg](https://qiita-image-store.s3.amazonaws.com/0/17400/3829331c-3153-01c9-0121-38fd28eb1c63.jpeg)

この画面に関しては、1つの画面をまとめるViewControllerの中にUIScrollViewを配置し、さらにその中にコンテンツ要素に該当する複数のContainerViewを配置しています。ContainerViewの親子間の処理の概要としては、それぞれのContainerViewにおいて表示するためのデータをReduxで管理し、親子間のView表示に関わる処理はProtocolを活用するようにしています。

![capture2.jpg](https://qiita-image-store.s3.amazonaws.com/0/17400/7c415c6f-9e1d-dc5e-aafd-a63737a3e9a1.jpeg)

遷移先の画面についてはここではダミーのものになりますが、カスタムトランジションを利用してサムネイル画像が浮き上がるような表現や遷移先の画面がバウンドするような表現を加えています。

![capture3.jpg](https://qiita-image-store.s3.amazonaws.com/0/17400/2b8894ba-24d2-4568-a9ee-f67726d3b29e.jpeg)

__環境やバージョンについて：__

+ Xcode10.1
+ Swift4.2
+ MacOS Mojave (Ver10.14)

__使用ライブラリ：__

Reduxを実現するためのライブラリやその他アプリ開発でよく使う定番ライブラリに加えてUI構築のためのライブラリも加えています。API通信のレスポンスや遅延を伴う処理のハンドリングを行う部分に関しては、実務では[Bolts-Swift](https://github.com/BoltsFramework/Bolts-Swift)を利用することが多いのですが、個人的にはよりシンプルで書きやすく感じたPromiseKitを使用しています。

| ライブラリ名 | ライブラリの機能概要 |
|:-----------|:------------|:------------|
|[ReSwift](https://github.com/ReSwift/ReSwift) |ReduxのSwift実装用 |
|[PromiseKit](https://github.com/mxcl/PromiseKit) |非同期通信のハンドリング |
|[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) |JSONデータの解析をしやすくする |
|[Alamofire](https://github.com/Alamofire/Alamofire) |HTTPないしはHTTPSのネットワーク通信用 |
|[AlamofireImage](https://github.com/Alamofire/AlamofireImage) |画像URLからの非同期での画像表示とキャッシュサポート |
|[RealmSwift](https://github.com/realm/realm-cocoa/tree/master/RealmSwift) |アプリ内のデータベース |
|[CalculateCalendarLogic](https://github.com/fumiyasac/handMadeCalendarAdvance) |日本の祝祭日の判定 |
|[KYNavigationProgress](https://github.com/ykyouhei/KYNavigationProgress) |UINavigationBar直下へのProgressBar表示 |

## 3. ReSwiftを用いたSwiftとReduxの処理関係のまとめ

ここからはReSwiftを用いてReduxの処理をアプリ内で実現するために、押さえておきたい事項やそれぞれの部分が担っている役割についてまとめていきます。Reduxは下記に示す3つの原則に則った上で状態管理のフローを縛ることによって状態管理を行う形になっています。

1. Single source of truth.
2. State is read-only.
3. Mutations are written as pure function.

下記に示しているのはReduxのデータフローの図解(※ベースはReSwiftのリポジトリに掲載されていたもの)になりますが、状態(State)の更新はAction経由でしか許可していない点とReducer内の処理によって状態の更新を実行する点、そしてView(ViewController)側では状態の更新通知を受け取る形になっている点がポイントになるかと思います。

<img width="735" alt="reswift_workflow.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/d71fbfe0-b45c-a83a-907f-d5e543ea20bc.png">


また、今回のサンプルアプリにおいてはActionを発行するためのメソッド群を別途ActionCreatorsというグループに分けています。メソッド内で必要なデータの取得や登録等の処理が実行された際には、その結果をActionを経由してReducerに引き渡し、更新されたStateを元にしてView要素の構築を行なっていくという一連の流れとなっています。

![redux_notebook.jpg](https://qiita-image-store.s3.amazonaws.com/0/17400/66aa1d42-1dea-94f8-b0ba-67b970cba422.jpeg)

このように取得データと画面におけるUIの状態との歩調を合わせるためにReduxでの状態管理を行い、 画面とStateの紐付けを行うためにViewControllerでStateの変更を関しするような形にしています。またReduxの処理フローをiOSアプリ内に組み込んでいく場合に注意しておくと良いポイントとしては、StateとViewControllerとの関わり方をどのように設計していくかという点にあると個人的に感じております。

+ [参考：なぜiOSアプリ開発でもReduxなのか](https://blog.fenrir-inc.com/jp/2017/08/ios-redux.html)

#### ★3-1. Redux処理における登場人物と基本事項の確認：

ここではReSwiftを利用してReduxの実装を行う際において必要なファイルと役割に関して解説します。今回のサンプルでは、下記のような形でReduxの処理を実現するために必要な要素を役割ごとのファイルに分割した上でまとめています。さらに命名によって画面ごとにそれぞれのStateが対応するようにしています。

<img width="901" alt="introduction_of_redux.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/c7a358d0-efcc-629e-8a55-bb725bf49a11.png">

上記の役割を担うためのそれぞれのファイルで定義するべきものの具体例をここから示して行くことにします。以降のコードは`PickupMessage(画像付きメッセージを表示する部分の状態や表示データの取得を行う)`におけるReduxの処理部分のものになります。

__① Stateに関する部分の実装例:__

まずは対応する画面の状態（`このサンプルにおいては画面表示に必要なデータ要素`）の定義を`ReSwift.StateType`プロトコルを適用したStructで下記のような形で記載します。

```swift:PickupMessageState.swift
import Foundation
import ReSwift

// ピックアップメッセージの状態に関するstateの定義
struct PickupMessageState: ReSwift.StateType {

    // ピックアップメッセージの一覧を格納する配列（初期値: []）
    var pickupMessageStateList: [PickupMessageEntity] = []

    // ピックアップメッセージの一覧エリアの表示フラグ（初期値: false）
    var isPickupMessageAreaHidden: Bool = false
}
```

このStateに設定されているプロパティの値を元に画面表示を組み立てますが、Stateを更新するための処理はActionを発行する処理を経由して行う形となる点がポイントになります。

__② Actionに関する部分の実装例:__

続いてそれぞれの画面に対応するStateを更新するための唯一の手段となるActionに関して見ていきます。`ReSwift.Action`プロトコルを適用したenumを該当するStateのextensionとして下記のような形で記載します。

```swift:PickupMessageAction.swift
import Foundation
import ReSwift

extension PickupMessageState {

    // ピックアップメッセージのstateを変更させるアクションをEnumで定義する
    enum pickupMessageAction: ReSwift.Action {

        // ピックアップメッセージの読み込み成功時にAPIより取得した値をセットするアクション
        case setPickupMessage(pickupMessage: [PickupMessageEntity])

        // ピックアップメッセージエリアの表示状態の値をセットするアクション
        case setIsPickupMessageAreaHidden(result: Bool)
    }
}
```

Stateの更新をするために必要となる値については、Actionで定義した引数にへ格納することで以降の処理に引き渡す形にしています。Stateを更新をして表示内容の変更を実行したい場合には、ViewController等で`appStore.dispatch(action)`のような形でStoreのdispatchメソッドを実行することで実現します。

__③ ActionCreatorに関する部分の実装例:__

ActionCreatorでは、Actionの引数をして渡す内容`（主に外部APIから取得した表示データや実行結果・アプリ内に格納しているデータ）`を作成する処理とActionの発行をする処理をセットにしてまとめた部分になります。下記の例は、該当部分の表示状態の設定や表示したいデータを取得する処理をまとめたものになります。

```swift:PickupMessageActionCreator.swift
import Foundation
import ReSwift

struct PickupMessageActionCreator {}

extension PickupMessageActionCreator {

    // ピックアップメッセージ表示エリアの表示状態を反映する
    static func shouldHidePickupMessageArea(result: Bool) {

        // Storeにあるdispatchメソッドを実行してStateの更新要求を送る
        appStore.dispatch(

            // 該当するStateのextentionとして定義したActionを実行する
            PickupMessageState.pickupMessageAction.setIsPickupMessageAreaHidden(result: result)
        )
    }

    // ピックアップメッセージを取得する
    static func fetchGourmetShopList() {

        // ピックアップメッセージのAPIから全件情報を取得する

        // MEMO: 
        // ① 各種APIのエンドポイントへのアクセスをする処理はAPIManagerクラスを別途用意している。
        // ② APIアクセスに関する処理ではSwiftyJSONとPromiseKitを併用している。
        // ③ Actionを発行する前にデータを取得や登録等の処理を実行して、その結果を一緒に引き渡す。

        APIManagerForPickupMessage.shared.getPickupMessageList()
            .done { messageJSON in

                // 成功時: 取得したJSONを解析したものを配列にしたものを引数に渡してアクションの実行
                let pickupMessageList = PickupMessage.getPickupMessagesBy(json: messageJSON)
                appStore.dispatch(PickupMessageState.pickupMessageAction.setPickupMessage(pickupMessage: pickupMessageList))
 
            }.catch { error in

                // 失敗時: 空配列を引数に渡してアクションの実行
                appStore.dispatch(PickupMessageState.pickupMessageAction.setPickupMessage(pickupMessage: []))
        }
    }
}
```

場合によってはViewController内で直接Actionを発行するケースもあると思いますが、このようにAlamofire等を利用した非同期通信を伴う処理やRealm等を利用したアプリ内データを利用する処理がAction発行前に必要な場合には一連の処理をひとまとめにしておくと良いかと思います。

__④ Reducerに関する部分の実装例:__

Action発行時の内容（引数として送られたデータ等も含む）と現在のStateの値を元に更新されたStateを作成する処理はReducerで実行します。Reducerについては該当するStateごとに設定されており、この部分の処理でしかStateの更新は許可されていない点に注意してください。Actionに定義した引数で受け取ったものをStateに反映する場合の処理の場合は下記のような形となります。

```swift:PickupMessageReducer.swift
import Foundation
import ReSwift

struct PickupMessageReducer {}

extension PickupMessageReducer {
    
    static func reducer(action: ReSwift.Action, state: PickupMessageState?) -> PickupMessageState {

        // ピックアップメッセージのstateを取得する(ない場合は初期状態とする)
        var state = state ?? PickupMessageState()
        
        // ピックアップメッセージのstateを変更させるアクションでない場合はstateの変更は許容しない
        guard let action = action as? PickupMessageState.pickupMessageAction else { return state }

        // ピックアップメッセージのstateを変更させるアクションに合わせてStateの更新を実行する
        switch action {

        case let .setPickupMessage(pickupMessage):
            state.pickupMessageStateList = pickupMessage

        case let .setIsPickupMessageAreaHidden(result):
            state.isPickupMessageAreaHidden = result
        }

        // Debug.
        AppLogger.printMessageForDebug("PickupMessageStateが更新されました。")

        return state
    }
}
```

ここで紹介しているReducerの処理はシンプルなものになりますが、画面の表示やコントロールに必要な要素(State)に関わる部分ですので「Actionを発行するとState内の該当の値が正しく反映されていること」をUnitTest等を利用して動作を担保するような形にすると更に良いかと思います。

__⑤ AppDelegateでのStoreの設定等の部分:__

①〜④では「とあるStateが更新されて新しいStateになるまでの概要」に関して説明しました。このアプリで管理しているすべての状態については1つのStore内で保持・管理されており、それぞれの画面やロジック部分で必要となるActionの発行やState値の利用の際には、`AppDelegate.swift内でグローバルに定義したStoreのインスタンス`から利用する形となります。

```swift:AppDelegate.swift
// Storeの定義をあらかじめAppDelegate.swiftのクラス外に下記のように追記しておく
let appStore = Store(reducer: appReduce, state: AppState(), middleware: [ActionLoggingMiddleware])

// PickupMessageReducerにてPickupMessageStateの更新が終わった後の処理の概要:
// ① PickupMessageStateの更新がStoreに反映された後は該当のViewController内でAppStateの更新を検知できるようする。
// ② AppStateの更新を検知した際に必要なメソッドの処理内にPickupMessageStateの値に合わせた実装を行なっていく。

// ※1 AppState()で行なっていること → PickupMessageStateをはじめ、このアプリで利用するStateを集約する
// ※2 AppReduceで行なっていること  → PickupMessageReducerをはじめ、このアプリで利用するReducerを集約する
// ※3 MiddlewareではActionの実行前後で割り込ませて実行したい処理を記載する（今回は発行されたアクションのログ出力のみ）

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    ・・・（省略）・・・
}
```

__⑥ 該当するViewControllerでのAppStateの更新を検知する部分:__

最後にStoreで管理されているStateが変更されたタイミングで、State値が更新された通知を画面に対応するViewControllerにて受け取れるようにするための処理は下記のような形になります。更新されたStateに合わせたView要素の表示に関する処理については、`func newState(state: AppState)`内に記載して「状態を元に表示内容を決める」形にしている点がポイントになるかと思います。

```swift:PickupMessageViewController.swift
class PickupMessageViewController: UIViewController {

    ・・・（省略）・・・

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Stateが更新された際に通知を検知できるようにするリスナーを登録する
        appStore.subscribe(self)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // Stateが更新された際に通知を検知できるようにするリスナーを解除する
        appStore.unsubscribe(self)
    }

    ・・・（省略）・・・
}

extension PickupMessageViewController: StoreSubscriber {

    // Stateの更新が検知された際に実行される処理
    func newState(state: AppState) {

        // TODO: PickupMessageStateの状態に合わせて配置したView要素を変更する処理を記載する

        ・・・（省略）・・・

        // Debug.
        AppLogger.printStateForDebug(state.pickupMessageState, viewController: self)
    }
}
```

本サンプルで利用しているReduxに関連した処理に関する構成ファイルの概要とStateの具体例に関しては下記のような形になっています。

<img width="971" alt="redux_files.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/669eb6ff-7e8b-8c97-1cf1-dba1f077da28.png">

画面ごとにReduxのデータフローを実現するための処理を分割しているためにファイル数は多くなりますが、実際のUIの動きを検証する際にStateの現在状態をデバッグログに表示しながらの確認もできたりするので、クラッシュするケースの再現時やイレギュラーな画面表示になる場合の検知等に上手に活用できるのではないかと思います。

#### ★3-2. ReSwiftで実現するRedux処理の概要図と処理フロー：

上記で解説したReduxの処理に必要な各々の処理で行われている内容と、処理フローをまとめると下記のような形の流れとなります。

<img width="987" alt="whole_lifecycles.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/fbf1578d-630f-12da-517c-86ae476f3a0a.png">

一見するとiOSアプリ開発でよくお目にかかるアーキテクチャとは相入れない印象を持つかもしれませんが、状態を格納しているオブジェクト（Stateが該当）の現在状態を元に各々の画面状態を構築するという形になる点に注目すると理解がしやすくなると思います。

#### ★3-3. StoreとState/Reducerの関連図と集約部分に関して：

今回のサンプルではそれぞれの画面に対応するStateを用意する形にしているので、それぞれのStateやReducer自体はなるべく複雑な形にはせずに集約するためのファイルやメソッドを下記のような形で定義しています。

<img width="964" alt="integrate_to_store.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/6a4bb8b7-0796-5824-07b8-0b081c603473.png">

複数のContainerViewを利用して、それぞれの画面要素ごとにViewControllerを分割して表示している部分が多い場合や項目が多い入力フォーム等では1つのViewControllerで状態管理をすると処理が煩雑になりやすいので、画面全体における状態の管理とUI処理を切り分けて考えることができる構成にするためにReduxの概念や考え方を取り入れるのも有効な選択肢の1つであるように思います。

## 4. このサンプルにおけるその他Reduxと関連する処理に関して

ここでは、本サンプルにおけるReduxのデータフローを実現するために必要なファイル構成部分以外の部分の処理に関する部分の補足説明になります。

#### ★4-1. Redux処理とプロトコルの使い分けについての解説：

基本的にはStateが更新されたタイミングを検知し、該当するStateの値を元に部品となるViewをはじめとする画面要素を構築していく形の構成になっていますが、ContainerViewを利用してそれぞれのViewControllerに対して親子関係があるような構成の部分では、__「子のViewControllerに配置されたボタンを押下した時に、親のViewControllerから次の画面を表示する画面遷移処理を実行する」__ という動きをしなければいけない場合があるかと思います。

このような処理をする必要が出てくる場合については、Reduxで部分で処理するのではなく下記のような意図でプロトルを用いて関連する画面に対するViewの処理を実行できるようにしています。

<img width="918" alt="redux_and_protocols.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/2c34e58a-e8c9-a4ad-8a22-5f71a8bebdf7.png">

`MainViewController.swift`で表示している月ごとのカレンダー表示部分において、日付のボタンを押下した場合に画面遷移をする場合を考えてみることにします。月ごとのカレンダー表示部分は親のViewControllerに配置したContainerViewから表示しており、ViewControllerを分割しています。

また今回は画面遷移処理のようにReduxで管理していない部分については、子のViewControllerから親のViewControllerに定義した画面遷移を実行できるようにプロトコルを活用して親子間の処理を連携できるようにしています。

![relationship_of_transition.png](https://qiita-image-store.s3.amazonaws.com/0/17400/783092ee-ae2f-fecf-0a3b-92abb092c39f.png)

__① ContainerViewとEmbedSegueで繋がっている子のViewControllerでの処理:__

```swift:MonthlyCalendarViewController.swift
// MARK: - Protocol

// MEMO: ContainerViewを介したViewに関する処理はプロトコル経由で接続する
protocol MonthlyCalendarViewDelegate: NSObjectProtocol {

    // 月別カレンダーのボタンタップ時にプロトコルを適用したViewController側で行うためのメソッド
    func selectMonthlyCalendar(selectedDate: (selectedYear: Int, selectedMonth: Int, selectedDay: Int))
}

class MonthlyCalendarViewController: UIViewController {

    ・・・（省略）・・・

    @objc private func calendarButtonTapped(button: UIButton) {

        // Debug.
        print("選択された日付:", "\(selectedYear!)年\(selectedMonth!)月\(button.tag)日")

        // カレンダーで選択された日付を取得して、プロトコルを適用しているViewControllerに受け渡す
        let targetSelectedDate: (selectedYear: Int, selectedMonth: Int, selectedDay: Int) = (
            selectedYear: selectedYear!,
            selectedMonth: selectedMonth!,
            selectedDay: button.tag
        )
        self.delegate?.selectMonthlyCalendar(selectedDate: targetSelectedDate)
    }

    ・・・（省略）・・・
}
```

__② ContainerViewを配置している親のViewControllerでの処理:__

```swift:MainViewController.swift

// MARK: - MonthlyCalendarViewDelegate

extension MainViewController: MonthlyCalendarViewDelegate {

    // 選択されたカレンダーの内容を反映させつつ、画面遷移を行う
    func selectMonthlyCalendar(selectedDate: (selectedYear: Int, selectedMonth: Int, selectedDay: Int)) {

        // 記事表示用の画面へ遷移する
        let storyboard = UIStoryboard(name: "DailyMemo", bundle: nil)
        let dailyMemoViewController = storyboard.instantiateViewController(withIdentifier: "DailyMemoViewController") as! DailyMemoViewController
        dailyMemoViewController.setSelectedDate(selectedDate)

        // カスタムトランジションのプロトコルを適用させて遷移する
        let navigationController = UINavigationController(rootViewController: dailyMemoViewController)
        navigationController.transitioningDelegate = self
        self.present(navigationController, animated: true, completion: nil)
    }
}

// MARK: - UIViewControllerTransitioningDelegate

extension MainViewController: UIViewControllerTransitioningDelegate {

    // MEMO: 適用しているカスタムトランジションの詳細については、DailyMemoTransition.swiftを参照

    // 進む場合のアニメーションの設定を行う
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        // 現在の画面サイズを引き渡して画面が縮むトランジションにする
        dailyMemoTransition.originalFrame = self.view.frame
        dailyMemoTransition.presenting = true
        return dailyMemoTransition
    }

    // 戻る場合のアニメーションの設定を行う
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        // 縮んだ状態から画面が戻るトランジションにする
        dailyMemoTransition.presenting = false
        return dailyMemoTransition
    }
}
```

#### ★4-2. 非同期処理やデータの取得処理とActionの発行を組み合わせる部分の解説：

続いてAPI通信の様な非同期処理を伴う部分のAction発行時の処理を考えてみます。API通信実行する部分についてはAPIのエンドポイントへアクセスした際の処理結果に応じた処理を実行するためのクラス（Singleton Instance）を定義しています。このクラス内の処理の概要としては、ActionCreatorで定義したStateを更新するためのActionを発行する処理と連動して扱えるようにするため、下記のような形にします。

<img width="918" alt="async_api_request.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/e7c04109-bf2d-03f3-d212-2ea622031ffc.png">

__PickupMessage(画像付きメッセージを表示する部分の状態や表示データの取得を行う)部分におけるAPI通信処理:__

```swift:APIManagerForPickupMessage.swift
import Foundation
import Alamofire
import PromiseKit
import SwiftyJSON

class APIManagerForPickupMessage {

    private let baseUrl = "APIのエンドポイントとなるURLを指定する"

    // MARK: - Singleton Instance

    static let shared = APIManagerForPickupMessage()

    private init() {}

    // MARK: - Functions

    // PickupMessage一覧を取得する
    func getPickupMessageList() -> Promise<JSON> {

        // Alamofireの非同期通信をPromiseKitの処理でラッピングする
        // 例.
        // -----------
        // getNewsList(page: 0)
        //    .done { json in
        //        // 受け取ったJSONに関する処理をする
        //        print(json)
        //    }
        //    .catch { error in
        //        // エラーを受け取った際の処理をする
        //        print(error.localizedDescription)
        //    }
        // -----------
        // 参考URL:
        // https://medium.com/@guerrix/101-alamofire-promisekit-671436726ff6
        // ※ Swift4系では書き方が変わっているのでご注意を!
        // https://stackoverflow.com/questions/48932536/swift4-error-cannot-convert-value-of-type-void-to-expected-argument-typ
        return Promise { seal in
            Alamofire.request(baseUrl, method: .get).validate().responseJSON { response in

                switch response.result {

                // 成功時の処理(表示に必要な部分だけを抜き出して返す)
                case .success(let response):

                    let res = JSON(response)
                    let json = res["article"]["contents"]
                    seal.fulfill(json)

                // 失敗時の処理(エラーの結果をそのまま返す)
                case .failure(let error):

                    seal.reject(error)
                }
            }
        }

    }
}
```

※ 本サンプルでは「API通信結果のハンドリング・任意の処理を実行したn秒後に別の処理を続けて実行する」部分を実現するためにライブラリ:PromiseKitを利用しています。

## 5. UI表現に関する処理とReduxに関する処理の組み合わせ

ここからは実際に本サンプル内で実装している、Reduxで実現する状態管理と画面表示を連結した処理部分の事例においてポイントとなりそうな部分を紹介していきたいと思います。

<img width="906" alt="casestudy_examples.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/0cbe9856-db45-ab53-26ee-84334aea21f4.png">

その中でも特にこの記事では、素直に実装した場合にViewControllerの状態やUIの管理が煩雑になりやすい3つのケースに関して図解を交えて簡単に説明できればと思います。
（詳細なレイアウト定義や実装に関しては、冒頭に掲載したリポジトリを参考にして頂けましたら幸いです）

#### ★5-1. 項目が多めな入力フォームを構築する場合の例：

まずは、初回に表示されるユーザーの情報を入力するフォーム部分のUIをReduxでの状態管理を組み合わせた例について見ていきます。

この画面におけるReduxを用いて管理したいものとしては、

+ この画面からユーザーが現在入力または選択している値の状態
+ 次の画面に進むための条件が満たされているかの判定
+ キーボードが表示されているかの判定

の3つとなり、上記の情報を踏まえてこの画面におけるStateを定義すると下記のような形になるかと思います。
Redux部分の実装としては、__「ユーザーの入力や選択する行動によってUIの状態が変化する = ユーザーのユーザーの入力や選択した値が見える状態となる」__ となる必要があります。  

<img width="949" alt="step1_of_chpter5_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/caaac1d5-a82a-e728-585b-962d38930353.png">

画面内に配置されているUIパーツからの入力または選択された値をStateへ反映する際は、`UITextFieldDelegate`や`UITextViewDelegate`をはじめとする各種Delegateに関する処理や`addTarget`メソッドで実行される処理内にActionCreatorで定義したStateを更新するためのActionを発行する処理を実行させることで実現します。

<img width="951" alt="step2_of_chpter5_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/34d28e91-0376-d53e-f63d-06b4f15bcbdf.png">

その後はReducerを経由して入力された値をStateに反映することで、Stateを更新してViewControllerで仕込んである`func newState(state: AppState)`から更新されたStateを受け取って、ユーザーが入力した値を反映します。この画面におけるReduxの処理は言い換えると __「入力されたデータをStateに反映させて、ViewControllerでStateの値をそのままUI要素へ反映する」__ 形になります。

<img width="957" alt="step3_of_chpter5_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/1686d89f-8f93-ff90-3529-c6de44e0eb12.png">

#### ★5-2. 次の10件を取得して現在の表示中の一覧に追加する場合の例：

次に、一番最初に10件の表示データが表示されている初期状態の画面において一番下にある「次の10件を表示する」ボタンを押下した際に現在表示されているデータの一番後ろから新たに取得した10件のデータを表示するようなUIを考えて見ていきます。

表示データの一覧を表示しているViewControllerに関しては下記のような形で、ContainerViewを利用してそれぞれの画面を分離しています。このViewControllerで実行する処理はAPIから次の10件の表示データを取得してこのViewControllerに対応するStateを更新して画面に反映する処理をしていきます。

<img width="948" alt="step1_of_chpter5_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/7b78766b-b937-3b5a-5d19-1be50ef12062.png">

しかしながら、これだけでは親のViewControllerに配置しているContainerViewの高さは変化しないので、このタイミングでStateで格納されている表示データの個数から高さを計算して反映させる処理を下記のような形でプロトコルを利用して実行させることで実現しています。

<img width="967" alt="step2_of_chpter5_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/0cae9052-80d9-19b7-c6c6-537d695dffeb.png">

この様な動きをする処理については、普通のUITableViewの処理だけで実現しようとする場合には、画面の状態まで詳細に考慮しようとするとなかなか面倒な実装になりますが、Reduxを用いた状態管理を利用することで変化をさせるべき部分の処理に関する見通しをよくすることができる様に思います。

#### ★5-3. 複数個のContainerViewへ親側の処理結果を伝える場合の例：

最後に、それぞれのコンテンツ要素を表示しているContainerViewを配置している親の画面をRefreshした場合の処理について見ていきます。(iOS10以降からUIScrollViewについても引っ張って更新する動きができるようになっています)

+ [（参考）[iOS 10] 全てのスクロールビューでRefresh Controlがサポートされました](https://dev.classmethod.jp/smartphone/ios-10-support-refresh-control-in-all-scroll-views/)

この場合に関しても、下記の様に __「それぞれの画面ごとに初期の状態に戻る処理をするのではなく、それぞれの画面に対応しているStateを初期の状態に戻すためのActionを発行する」__ ことで実現できます。

<img width="958" alt="step1_of_chpter5_3.png" src="https://qiita-image-store.s3.amazonaws.com/0/17400/9bcb8987-420e-179e-553a-8a1f6a10dbe4.png">

UI上で実現したい動き次第ではViewController内だけで処理すると骨が折れてしまいそうな実装でも、Reduxの処理と組み合わせることで、データの更新フローの整理と表示データに関する状態とUI表示に関する状態の関係性を整理できる様な例はまだまだあるかと思いますので、この部分に関しては引き続き模索して行こうと思います。

## 6. XCTestを活用してReduxのデータフローを担保するためのヒント

今回のサンプルではActionCreator内に定義したメソッドにおいてViewの表示に必要なデータを取得する処理を実行し、Action発行時にReducerにデータを送ることでView表示に必要なStateへ反映する形を取っています。

その中でもReduxのデータフローにおける __「Store(変更後のState) → Action → Reducer → Store(変更後のState)」__ の一連の流れを行う処理を担保したかったので、下記のような形でテストコードを実装しました。

#### ★6-1. Actionに定義された引数の値をStateに反映する部分のテスト：

それぞれのActionを発行する前後におけるStateの変化を反映する処理が担保されている例を見てみようと思います。Stateを更新するために取得したデータはActionを発行する際に引数として渡され、その後Reducerでの処理を経て新しいStateとなります。この処理のテストコードは下記のような形となります。

任意のStoreをコード内に準備して、検証したいStateに関するAction発行前後のStateの変化が正しく実行されていることを確認することがポイントになるかと思います。

```swift:ReduxSampleSwiftTests.swift
// 初回設定に関するStateにおいてActionの発行から値反映までが実行されているかを確認するテストコード
func testInitialSettingState() {

    let appStore = Store(reducer: appReduce, state: AppState(), middleware: [ActionLoggingMiddleware])

    // Action発行前の値に関するテスト
    let beforeInstallAppDate = appStore.state.tutorialState.installAppDate
    let beforeIsFinishedUserSetting = appStore.state.tutorialState.isFinishedUserSetting
    let beforeIsFinishedTutorial = appStore.state.tutorialState.isFinishedTutorial

    XCTAssertEqual(nil, beforeInstallAppDate, "最初はinstallAppDateがnilである")
    XCTAssertEqual(false, beforeIsFinishedUserSetting, "最初はisFinishedUserSettingがfalseである")
    XCTAssertEqual(false, beforeIsFinishedTutorial, "最初はisFinishedTutorialがfalseである")

    // アクションの実行 ※InitialSettingActionCreator.setCurrentUserStatus()
    let currentDate = Date()
    appStore.dispatch(
        TutorialState.tutorialAction.setInstallAppDate(date: currentDate)
    )
    appStore.dispatch(
        TutorialState.tutorialAction.setIsFinishedTutorial(result: true)
    )
    appStore.dispatch(
        TutorialState.tutorialAction.setIsFinishedUserSetting(result: true)
    )

    // Action発行後の値に関するテスト
    let afterInstallAppDate = appStore.state.tutorialState.installAppDate
    let afterIsFinishedUserSetting = appStore.state.tutorialState.isFinishedUserSetting
    let afterIsFinishedTutorial = appStore.state.tutorialState.isFinishedTutorial

    XCTAssertEqual(currentDate, afterInstallAppDate, "installAppDateにAction経由で現在時刻が反映される")
    XCTAssertEqual(true, afterIsFinishedUserSetting, "isFinishedUserSettingにAction経由でtrueが反映される")
    XCTAssertEqual(true, afterIsFinishedTutorial, "isFinishedTutorialにAction経由でtrueが反映される")
}
```

コード例は、アプリの状態に関する値の反映に関するRedux処理部分のものになりますが、ユーザーが入力した値をStateに反映する処理についても冗長な感じではありますがほぼ同様のテストコードを加えています。（基本的にはStateの値を元にUIやViewの状態が作成される形です）

#### ★6-2. API通信時に取得した値をStateに反映する部分のテスト：

本サンプル内で使用しているActionCreator内に定義されたメソッドにおいては、APIの非同期通信を利用するものがあります。このような部分の処理に関しては、APIから取得したJSONデータを加工したものをAction発行時に引き渡す必要があるので、別途レスポンスで取得した値と同じ形式のJSONデータをスタブとして持ち、そのデータを利用すると良いかと思います。この処理のテストコードは下記のような形となります。

```swift:ReduxSampleSwiftTests.swift
// ピックアップメッセージ画面に関するStateにおいてActionの発行から値反映までが実行されているかを確認するテスト
func testPickupMessageState() {

    let appStore = Store(reducer: appReduce, state: AppState(), middleware: [ActionLoggingMiddleware])

    // Action発行前の値に関するテスト
    let beforeIsPickupMessageAreaHidden = appStore.state.pickupMessageState.isPickupMessageAreaHidden
    let beforePickupMessageStateList = appStore.state.pickupMessageState.pickupMessageStateList

    XCTAssertEqual(false, beforeIsPickupMessageAreaHidden, "初期値はfalseである")
    XCTAssertEqual(0, beforePickupMessageStateList.count, "初期値は空配列である")

    // PickupMessageActionCreator内に定義した値を反映するアクションの実行
    appStore.dispatch(
        PickupMessageState.pickupMessageAction.setIsPickupMessageAreaHidden(result: true)
    )

    // MEMO: スタブで用意した形式のJSONファイルを読み込んでAction実行時にJSONの値を反映させる
    // ※ エンドポイントとほぼ同じレスポンスのJSONファイルを準備する
    let filePath = Bundle.main.url(forResource: "PickupMessageStub", withExtension: "json")!
    let messageJSON = JSON(try! Data(contentsOf: filePath))
    let pickupMessageList = PickupMessage.getPickupMessagesBy(json: messageJSON["article"]["contents"])

    appStore.dispatch(
        PickupMessageState.pickupMessageAction.setPickupMessage(pickupMessage: pickupMessageList)
    )

    // Action発行後の値に関するテスト
    let afterIsPickupMessageAreaHidden = appStore.state.pickupMessageState.isPickupMessageAreaHidden
    let afterPickupMessageStateList = appStore.state.pickupMessageState.pickupMessageStateList

    // (1) 表示フラグの変更
    XCTAssertEqual(true, afterIsPickupMessageAreaHidden, "setIsPickupMessageAreaHiddenの引数と同じ値となる")

    // (2) JSONからの表示用データ生成
    XCTAssertEqual(18, afterPickupMessageStateList.count, "pickupMessageStub.json内の表示用データの個数と同じ値となる")

    // (3) 表示用データの構成要素
    let firstData: PickupMessageEntity = afterPickupMessageStateList.first!
    XCTAssertEqual("1", firstData.id, "pickupMessageStub.jsonのデータ部分の1番目に相当するID文字列と同じ値となる")
    XCTAssertEqual("旬の魚が安く手に入る「いきいき魚市」", firstData.title, "pickupMessageStub.jsonのデータ部分の1番目に相当するタイトル名と同じ値となる")
    XCTAssertEqual("ショッピング・お買い物", firstData.category, "pickupMessageStub.jsonのデータ部分の1番目に相当するカテゴリ名と同じ値となる")
    XCTAssertEqual("https://kanazawa-photos.s3-ap-northeast-1.amazonaws.com/articles/images/1/large.jpg", firstData.imageUrl, "pickupMessageStub.jsonのデータ部分の1番目に相当する画像ファイルURLの文字列と同じ値となる")
}
```

特にReSwiftで実現するReduxのデータフローを担保するためのテストコードを用意しておくことで、画面状態を構成するために必要な値をAction経由で渡した際に然るべき形でStateに反映されていることを担保しておくと実装がより捗るかと思います。（機能を担保するためのテストコードの形は様々な形があると思いますが、状態の更新を伴う処理に関しては重要な部分なのでこのサンプルではこのような形にしました）

+ [参考:　Getting started with Redux in Swift - Part 1](https://medium.com/mackmobile/getting-started-with-redux-in-swift-54e00f323e2b)

## 7. あとがき

この記事の中ではReduxの活用とUI実装の組み合わせに関するトピックに関して、自分なりに簡単なサンプル実装を通じての解説をしましたが、今後はさらにより複雑な画面構成のパターンやRxSwift等とも組み合わせたアーキテクチャの事例に関しても深掘りができるよう突き詰めていければと感じました。

Reduxのルールや原則はiOSが本来持っている仕組みとは異なる部分もあるので、その部分に関しては理解するまでは若干のしんどさはありましたが、Stateの値の状態に応じてアプリのUI要素がStateに対応した形になる、すなわち __「Stateの値 = アプリのUI要素の状態」__ として結びついていることを念頭に置きながら進めていくとより理解が深められるのではないかと思います。

サンプルを作成する過程の中ではReSwiftで実現できるReduxの構成はもちろんですが、Stateの変化とUIの変化を結びつける上でのStateの構造設計やUI間での関連する処理の設計に関しても、__「各状態におけるデータとUIのあるべき姿を整理する」__ ことが大切になってきます。その中でも特に、

+ ReSwift(Redux)のStateで管理したい部分はどこか？
+ データに関わる部分とUIの変更処理の連携を実現するにあたりReduxやProtocolをいかに利用するか？

の2点については、設計時ないしは実装時に方針を予め立ててから進めていくと良いかもしれません。

UI実装をする際においては、アプリ内の機能が増えていくことに伴ってUI実装においても状態管理がどんどん煩雑化しやすく、特に状態と紐づくUIの考慮漏れについては複雑になるほどに検知がしにくくなる部分になるかと思います。このような問題に対応する場合の解決策の選択肢の1つとして、状態とUI要素の関連性を整理するためにReduxを利用する方法は有効なのではないかと個人的に感じた次第です（今関わっているプロジェクトやこれから作る個人アプリでも導入してみたいなぁ...という願望もあります）。