これまで自分が実装してきたMVPは中途半端だった

# はじめに
今回がQiita初投稿です  
普段は，Swift・Python・Goあたりを触っている執筆者です  
生暖かい目線でお願いします

## 執筆したきっかけ
- MVVMやFluxを学ぶ前に，MVCとMVPを復習した
- [iOSアプリ設計パターン](https://peaks.cc/books/iOS_architecture)を読んだ結果，悔い改めることになったため
- 備忘録として残したかったから

## 読者に求めること
- iOSエンジニアである必要はありません
- ただし，Appleにおける○○という表現はします
- アーキテクチャって？とかアーキテクチャ．．．なるほどよくわからん
って方（一緒に勉強していきましょう!!）
- [iOSアプリ設計パターン](https://peaks.cc/books/iOS_architecture)を手元に置きながら，読んでほしいです！

# MVC
あまり多くは触れませんが，MVPを述べるためには必要不可欠な知識であるため書きます  
AppleにおけるMVCは少し特殊です．原初MVCと何が違うかについては，表を示した上で説明します．

## 原初MVC
|レイヤー  |役割  |
|---|---|
|Controller  |ユーザの入力を受けて，Modelに変更指示を送る  |
|Model  |指示を受けて，自身を更新する  |
|View  |Modelの変更を監視して，検知したら自身を更新する  |

特徴として，[iOSアプリ設計パターン](https://peaks.cc/books/iOS_architecture)では以下のように述べられています．  
>- ウィジェット単位でプレゼンテーションとロジックを分ける  
- Modelの変更に対し，オブザーバー同期が行われる  

## AppleにおけるMVC
|レイヤー  |役割  |
|---|---|
|Controller  |ユーザの入力を受けて，Modelに変更指示を送る．<br>Modelの変更を監視して，検知したらViewを更新する．  |
|Model  |指示を受けて，自身を更新する  |
|View  |画面の描画担当  |

大きな違いはViewとModelの関係でしょうか．原初MVCではView自身がModelを監視していましたが，AppleのMVCでは，Modelの監視はControllerの役割です．  
このようになった背景として，[iOSアプリ設計パターン](https://peaks.cc/books/iOS_architecture)では以下のように述べられています．  
>複雑なGUIやタッチスクリーンの登場により，「入力」と「出力」の境界線が曖昧になってきた．また，複雑になってきたUIに一貫性を持たせるためには，Viewの再利用が不可欠である．さらに，ViewとModelを完全分離したいという声も出てきた

このような背景から，ViewControllerとなったのでしょうか．．．？（まだ曖昧です）
なんにしても，xibによってUIViewやTableCellが表現できることも，上記のような要因がありそうですね

# MVP
ここまできて，ようやくMVPの話になります．

|レイヤー  |役割  |
|---|---|
|Presenter  |ユーザの入力を受けて，Modelに変更指示を送る．<br>Modelからのコールバックを受けて，Viewを更新する．  |
|Model  |指示を受けて，自身を更新する<br>変更後，コールバックなどを行う  |
|View  |画面の描画担当  |

ん？AppleにおけるMVCと酷似していますよね，そうなんです．これについて[iOSアプリ設計パターン](https://peaks.cc/books/iOS_architecture)では以下のように述べられています．  
> 設計変更の動機，レイヤーの役割のどちらから見てもこれはMVP（Passive View）と同じパターンだと考えてよいでしょう

## MVPを採用するメリットは？
これに対して私は，以下のように考えています.
- ViewControllerの責務を分けられる
- テストが書きやすい
- protocolによって，レイヤー間の依存性を薄めることができる
- 個人，学生チームによるアプリ開発の規模感にはMVVM以上はオーバーなことが多い
- iOSに限ってですが，アーキテクチャって？の人が学ぶ1つ目にはちょうどいい複雑さ

## 悔い改める必要があったポイント
- protocolによって，レイヤー間の依存性を薄めることができる

主にこの部分です．後はModelの意味合いです．

### protocolによって，レイヤー間の依存性を薄めることができる
MVCはオブザーバー同期に対して，MVPはフロー同期です．（Supervising Controllerについては割愛させていただきます）そのため，デザインパターンの1つであるDelegateを使用して，レイヤー間に関係を持たせます．  
なお，Delegateについては，[【swift】イラストで分かる！具体的なDelegateの使い方。](https://qiita.com/narukun/items/326bd50a78cf34371169)がとてもわかり易いです！！  
しかし，自分がこれまで書いてきたコードを見ると，中途半端であることがわかりました

```Swift:SampleViewController.swift
import UIKit

protocol SampleProtocol: class {
    func reloadFeed()
}

class SampleViewController: UIViewController {

    private var presenter: SamplePresenter!

    override func viewDidLoad() {
        super.viewDidLoad()
        setHoge: do {
            presenter = SamplePresenter(view: self)
            presenter.callGetSample()
        }
    }


}

extension SampleViewController: SampleProtocol {

    func reloadFeed() {
        print("hoge")
    }

}
```

```Swift:SamplePresenter.swift
import Foundation

struct SampleModel: Codable {
    let name: String
    let email: String
}


final class SamplePresenter {
    typealias View = SampleProtocol & SampleViewController
    private var state: loadStatus = .initial
    private weak var view: View?
    private var contentsList: [SampleModel] = []

    var numberOfSampleModel: Int {
        return contentsList.count
    }

    init(view: View) {
        self.view = view
    }

    func sample(at index: Int) -> SampleModel? {
        guard index < contentsList.count else { return nil }
        return contentsList[index]
    }

    func callGetSample() {
        defer {
            DispatchQueue.main.async {
                self.view?.reloadFeed()
            }
        }
        getSample(after: { str in
            self.contentsList = str
        })
    }

    func callPostSample() {
        defer {
            DispatchQueue.main.async {
                self.view?.reloadFeed()
            }
        }
        postSample(after: { str in
            self.contentsList = str
        },body: "hoge")
    }


    private func getSample(after: @escaping ([SampleModel]) -> Void) {

    }

    private func postSample(after: @escaping ([SampleModel]) -> (), body: String) {

    }

}

```
上記のコードにはたくさんの問題があります．  
- View(ViewController)が直接 Presenterのメソッドを呼んでいる
- Presenter内にAPI通信を行うメソッドが書かれている

他にもたくさんありますが，大きな問題は上記だと思います．  
そこで，本を読んだ上で以下のように修正してみました．

```Swift:SampleViewController.swift
import UIKit


class SampleViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    let model = SampleModel()
    private var presenter: SamplePresenterInput!

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter = SamplePresenter(view: self, model: model)
        label.text = "test"

    }

    @IBAction func onGetTapped(_ sender: Any) {
        presenter.onGetTapped()
    }

    @IBAction func onPostTapped(_ sender: Any) {
        presenter.onPostTapped(body: label.text!)
    }

}

extension SampleViewController: SamplePresenterOutput {

    func setName(name: String) {
        print("setName")
    }

    func setEmail(email: String) {
        print("setEmail")
    }
}

```

```Swift:SamplePresenter.swift
import Foundation

protocol SamplePresenterInput: class {
    var numberOfSampleModel: Int { get }
    func user(at index: Int) -> UserModel?
    func onGetTapped()
    func onPostTapped(body: String)
}

protocol SamplePresenterOutput: class {
    func setName(name: String)
    func setEmail(email: String)
}

final class SamplePresenter: SamplePresenterInput {

    private weak var view: SamplePresenterOutput!
    private var model: SampleModelInput
    private var contentsList: [UserModel] = []

    init(view: SamplePresenterOutput, model: SampleModelInput) {
        self.view = view
        self.model = model
    }

    var numberOfSampleModel: Int {
        return contentsList.count
    }

    func user(at index: Int) -> UserModel? {
        guard index < contentsList.count else { return nil }
        return contentsList[index]
    }

    func onGetTapped() {
        model.getSample(completion: { result in
            self.view.setName(name: result.name)
            self.view.setEmail(email: result.email)
        })
    }

    func onPostTapped(body: String) {
        model.postSample(completion: { result in
            self.view.setName(name: result.name)
            self.view.setEmail(email: result.email)
        }, body: body)
    }

}

```

```Swift:SampleModel.swift
import Foundation

protocol SampleModelInput {
    func getSample(completion: @escaping (UserModel) -> ())
    func postSample(completion: @escaping (UserModel) -> (), body: String)
}

struct UserModel: Codable {
    let name: String
    let email: String
}

final class SampleModel: SampleModelInput {

    let testUser = UserModel(name: "bob", email: "bob@hogehoge")

    func getSample(completion: @escaping (UserModel) -> ()) {
        print("get")
        completion(testUser)
    }

    func postSample(completion: @escaping (UserModel) -> (), body: String) {
        print("post")
        completion(testUser)
    }

}

```

UILabel周りとか，APIを想定しているのにgetとpostがほほ同じことは目をつぶってください  
注目して欲しい箇所は，各レイヤーがprotocolによって繋がれていて，疎結合になっていることです．  
互いをほぼ意識することなく，テストが書きやすくなっているはずです．  
また，適切な役割をModelに担わせることができました．

# 最後に
まだ理解できていない箇所があります．

```Swift
let model = SampleModel()
private var presenter: SamplePresenterInput!

override func viewDidLoad() {
    super.viewDidLoad()
    presenter = SamplePresenter(view: self, model: model)
    label.text = "test"

}
```
この部分です．特にModelに関する記述は．ViewではなくPresenterに書くべきなのかなと思っています．  
もっと勉強していきます！！最後まで読んでいただき，ありがとうございました！！