【Swift】MVPアーキテクチャについて色々調べてみた

#はじめに

[iOSアプリ設計パターン入門](https://peaks.cc/books/iOS_architecture)という書籍を参考に記事を作成しました。
とても読みやすい本でしたのでぜひご覧ください。

通常3,500円で電子が3,200円になります。

#MVPアーキテクチャ

Swiftで使われるアーキテクチャは、MVCやMVP、MVVMなどさまざまですが、
今回はMVPについて解説していきます。

**MVP**は画面の描画処理とプレゼンテーションロジックとを分離するアーキテクチャになり、
`Passive View`と`Supervising Controller`の二つのパターンが存在します。
この二つのパターンについては後ほど解説します。
MVPは、Model・View・Presenterの頭文字をとってMVPと言われています。

MVPと似ているアーキテクチャとしてあげられるMVCですが、
MVCとMVPのどちらがいいかと思う方もいると思います。

その時次第で変わるのかなと素人目線では思うのですが、
書籍を読む限り、テストを行ったり作業分担を行う際にはMVPを使用するというイメージで良いのではないかなと思います。

というのも、MVPの目的は**テストを容易に行えるようにすること**、**作業分担をしやすくすること**だからです。

##データの同期方法
MVPを語る上で大切になってくるのがデータの同期方法です。

MVPでは、主に二つの同期方法をとっています。
それは**フロー同期**と**オブザーバー同期**です。

###フロー同期

フロー同期は、**上位レイヤーのデータを下位レイヤーに都度セットしてデータを同期する手続き的な同期方法**です。

フロー同期のメリットとしてあげられるのは、
**データの流れを掴みやすいこと**になります。

なぜかというと、フロー同期は画面遷移の際の`push`や`pop`の時にデータを同期させるからです。
第三者が見ても、画面遷移のコード近辺を見ればどのデータを渡しているかが容易に判断できます。

ですが、逆にデメリットもあります。
例えば、画面A -> 画面B -> 画面Cと遷移する際に、
画面Aと画面Cでは同じデータを使いたい場合があるとします。

この際、画面Aから画面Cに値を渡すことができず、画面Bを経由しなければなりません。
画面Bではそのデータを使う予定はないので、必要のないデータを保持することになります。

これは、管理が煩雑する可能性があるのであまりよろしくないと思います。

###オブザーバー同期

オブザーバー同期は、**監視元である下位レイヤーが監視先である上位レイヤーから
Observerを使って送られるイベント通知を受け取ってデータを同期させる、宣言的な同期方法**です。

オブザーバー同期のメリットとしてあげられるのは、
**共通した監視先を持つ複数の箇所で、データを同期しやすいこと**です。

複数のタブや階層が離れている画面から共通の画面領域を監視しているので、
同期箇所で他の画面の参照を持つ必要がありません。

Twitterの「いいね」機能を例にすると分かりやすいかもしれません。
ユーザAがユーザBのツイートをいいねした時の処理を例にします。

**ユーザAの画面**
・ユーザBツイートのハートマークが赤になる
・ユーザAのプロフィールのいいね欄に追加される

**ユーザBの画面**
・いいねされましたの通知がくる
・自分のツイートのいいねが１つ増える

**ユーザBをフォローしている人の画面**
・ユーザBのツイートのいいねの数が１つ増える

ユーザAやユーザBの内容はおそらく別タブで行われているので、このような時に有効かと思います。
（もし間違っていたらすみません・・・！）

オブザーバー同期のデメリットとしては、
データが同期されるたびに変更処理を行うためいつデータが同期されるかが追いづらくなることです。

フロー同期の際は、画面遷移の時に同期されると分かっているので判断は容易ですが、
フロー同期の場合はそのように判断できないことがデメリットです。


##MVPにおけるデータの同期方法

先ほど、MVPには二つのパターンがあります。と記載しましたがそれについて少し説明したいと思います。
`Passive View`と`Supervising Controller`ですね。

**Passive View**は、Presenter -> View間に先ほど説明したフロー同期をします。

**Supervising Controller**は二つの同期方法を使います。
Presenter -> View間はフロー同期し、Model -> View間はオブザーバー同期します。

単純な私は、これなら全部Supervising Controllerでいいじゃないか！と思ってしまいますが、
実際はそうでもないらしいです・・・。（設計の世界は奥が深い！！）


#MVPの構造

Passive ViewとSupervising Controllerはそれぞれ違ったデータ同期方法ですが、
共通する事項も多く存在するのでそれについて記載していきます。

MVPには、**Model** / **View** / **Presenter** の3つのコンポーネントが存在します。
![スクリーンショット 2021-02-27 13.29.43.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/b8f8df64-089b-7d2b-299d-1a0b411bc1da.jpeg)

それぞれの共通事項は下記のようになります。

##共通事項
###Model

Modelは、UIに関係しない純粋なロジックやデータを持ちます。
MVPにおけるModelはMVCやMVVMと同じ役割を果たしています。

Modelが扱う領域の具体例は WebAPI やデータベースへの
アクセス、BLE デバイス制御や、会員ステータスごとの商品の割引率の計算などが存在するらしいです。

また、Modelの特徴として、ViewやPresenterに依存していないので、
Modelのみでもビルドが可能であるという特徴もあげられます。

###View

Viewは、ユーザの操作受付と、画面表示を担当するコンポーネントです

Viewは、タップやスワイプなどの操作を受け付けて、
Presenterに処理を委譲したりModelの処理を呼びたしたりします。

Modelに変更が発生したら、何らかの方法でViewが更新されるわけですが、
その時の更新方法の違いがPassive ViewとSupervising Controllerの違いになります。

###Presenter

Presenterは、ViewとModelの仲介役です。

Modelはアプリのビジネスロジックを知っていますが、
それが画面上にどのように表示されるかを知っているべきではありません。

Viewをシンプルにするために、複雑なロジックを持たせたくはないが、
Modelに画面表示に関わるロジックを持たせたくない場合に使用するらしいです。

Presenterは、たいてい１つのViewにつき１つ作成します。


##Passive View

Passive ViewはViewを完全に受け身にするパターンで
各コンポーネントのデータのやりとりはフロー同期によって実現されます。

つまり、次のような状態です。
![passiviView.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/497ad91b-f469-9ffa-7615-cc62d20ce151.png)

Passive Viewの利点は、Viewに描画処理の実装のみをも持たせるようにし、描画指示をPresenterに任せることで、
プレゼンテーションロジックのテストが行いやすくなる点だそうです。

なので、Passivi Viewにおける各コンポーネントの役割は下記のようになります。

| コンポーネント | 役割 |
|:-:|:-:|
|  Model |  Presenterからのみアクセスされ、Viewとは直接の関わりを持たない |
|  View |  Presenterからの描画指示に従うだけで、完全な受け身な立ち位置 |
|  Presenter |  全てのプレゼンテーションロジックを受け持つ |

##Supervising Controller

Supervising Controllerは、フロー同期とオブザーバー同期の両方を使うパターンです。
ViewはPresenterとはフロー同期で、Modelとはオブザーバー同期でデータのやりとりを行います。

つまりこのような形になります。
![supervisingController.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/b94f67fd-8863-7725-3058-9d3b2db982d2.png)

Supervising Controllerは、基本的にViewに対する入力イベントはPresenterに渡し、
必要に応じてViewに対して描画指示を出します。

Supervising Controllerの面白いところは、
Viewは簡単なプレゼンテーションロジックを持ちPresenterは複雑なプレゼンテーションロジックを持つというところです。

簡単な処理を行う時にわざわざPresenterを通すと冗長なので簡単な処理の場合は直接Modelからアクションを行います。

Supervising Controllerにおける各コンポーネントの役割は下記のようになります。

| コンポーネント | 役割 |
|:-:|:-:|
|  Model |  Presenterからのみアクセスされ、必要に応じてViewに対してイベントを通知する |
|  View |  PresenterとModelの双方から描画指示を受け、簡単なプレゼンテーションロジックを持つ |
|  Presenter |  複雑なプレゼンテーションロジックを担う |


##MVPの構造まとめ
Passive Viewの流れと、Supervising Controllerの流れは下記のようになります。

**Passive View**
View -> Presenter -> Model -> Presenter -> View

**Supervising Controller**
・簡単な処理
View -> Presenter -> Model -> View
・複雑な処理
View -> Presenter -> Model -> Presenter -> View

では、どちらのパターンを使用すればいいかというところですが、
書籍では迷ったらPassive Viewを使うようにと進めています。

理由としては、Supervising Controllerの特徴の一つである
「Viewが持つ簡単なプレゼンテーションロジック」が関係してきます。

まず、この簡単の基準を開発者間で統一することが難しいです。
開発者にはプロもいればそうでない人もいるので当たり前のことですね。

その他にも、オブザーバー同期によるModelとViewの接続に使う、適切なイベントの粒度の設計が難しい。
という点もあげられていました。

私自身が経験したことはないのでこちらに関しては深く語れませんが、
確かに大規模な開発になると進むにつれボロが出てくる可能性はありそうです。

これらのことから、とりあえずSupervising ControllerではなくPassive Viewでの開発をメインにすればいいかと思います。

設計に携わるエンジニアの方々は本当に尊敬します・・・。

#さいごに

今まで私は、MVCで色々開発してきましたが、
テストは切っても切り離せないことですし、チームで作業を行うにあたり、作業分担も大事になってきます。

なので、これからはMVPで開発する癖をつけていこうかなと思います。

MVPの中にも二つのパターンがありましたが、
私はとりあえずPassivi Viewをメインに学習を進めていこうと思います。
みなさんも一緒にがんばりましょう！！

この記事で学んだことと、書籍の内容をもとに
サンプルアプリを作成してみましたので下記の記事も合わせてご覧になっていただけると幸いです。

・[【Swift】MVPでYoutubeの動画検索アプリを開発してみた](https://qiita.com/Kai_82/items/1b2357340f668d7b0c38)

また、今回参考にした書籍はこちらになります。
設計パターンについての書籍の中でも一番分かりやすい気がしましたのでぜひご覧ください。

・[iOSアプリ設計パターン入門](https://peaks.cc/books/iOS_architecture)

以上、最後までご覧いただきありがとうございました。