【iOS × MVC】MVC アーキテクチャとは【コードあり コピペでOK】

# 目的
iOS開発における、MVCアーキテクチャを理解すること
筆者の備忘録


# 流れ
1. MVCの誕生
2. MVCの目的
3. 各レイヤーの役割
4. MVCの課題
5. (補足)Presentation Modelパターン
6. 画像表示アプリでMVCの実装 ← コードつき, コピペでOK


# 1. MVCの誕生
ここではMVCが誕生するまでの流れを説明していきます。

コード(:ViewControllerのようなもの) を ウィジェット(:ボタンやテーブル等のUIコンポーネント) に全て記載していた
↓
`メンタルモデル`と違うものになってしまう
↓
1979年にUIの表示をEditor、メンタルモデルをModelと名付けて、責務を分けた。
↓
その後、Editorを「入力に属するもの(Controller)」「出力に属するもの(View)」に分けた。
↓
View-Controller-Model:`MVC`の誕生

#### 補足
`メンタルモデル`とは
**人間が無自覚のうちに持っている、思い込みや価値観**
(例:画面は画面のプログラム、処理は処理のプログラムがあるはずだ)


# 2. MVCの目的

開発時に、`ドメインとプレゼンテーションを分けて考えること`
→ アーキテクチャ全般に言えること

#### 補足
`ドメイン =Model`
システム本来の関心領域
(例:足し算の処理)

`プレゼンテーション =View`
UIに関係するもの
(例:足し算の結果を画面に描画)


# 3. 各レイヤーの役割
ここでは、MVCの`Model``View``Controller`について、それぞれ役割の説明をしていく

| レイヤー | 役割 |
| --- | --- |
| Model | コマンドを受けて処理を行い、変数を更新 |
| View | Modelの変更を監視し、画面の更新 | 
| Controller | ユーザーの入力を受けて、Modelにコマンドを送る | 

<img width = 400 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/1897729f-bed7-cc38-7f38-8d532a92033b.png">
↑[1]より参考

`ControllerとViewによって構成されるウィジェットが、保持するModelにコマンドを送り、その変更をウィジェットに反映する`というもの。
Modelは、複数のViewやController 共有される。


# 4. MVCの課題
MVCモデルの課題としては以下の3点が挙げられます。

**① プレゼンテーションロジックの表現ができない**
年齢を表すModelを考える。あるViewでは「未成年なら赤文字、青年なら青文字」という処理が行われる。また、別のViewでは「還暦以上なら緑文字、還暦未満なら黄色文字」という処理。この「年齢で文字色を変更する という処理」はどこで表現すれば良いのか？

**② プレゼンテーション(View)状態を保持できない**
「表の選択状態」や「全てのフォームを埋めることで変化するボタン」等の、Viewから得られた情報の管理。

**③ テストが難しい**

↓①・②の説明
<img width = 600 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/d984f58d-79ce-f357-46dd-fd3446aeced1.png">


### 課題の解決策
上で挙げた課題に対して 以下のような解決策 が考えられます。
この3つの解決策の中でも、`Presentation Model`は、良い意味で大きな影響を与えました。

**継承**
プレゼンテーションロジックごとに新しいウィジェットのサブクラスを作る

**Pluggable Views**
様々な画面を詰め込んだViewクラスを用意して、そこに大量の引数を与えて表示する画面の振る舞いを変える

**Presentation Model**
View と Model の間にViewの状態を管理する層を追加する


# (補足)5. Presentation Modelパターン

Presentation Modelパターン は、　MVC は異なるアーキテクチャである。
ViewとModelの間に Presentation Model というレイヤーを置く。そこに `プレゼンテーションロジックやUIの状態管理`を担わせる。

<img width = 600 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/9b27b321-5fd4-412b-0894-dd64dec048e6.png">
↑[1]より参考
<img width = 600 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/783de38a-0d35-bfcf-ba1f-5c392b7fbce5.png">
↑[1]より参考

Presentation Modelパターン の`Model``View``Presentation Model``Controller`について、それぞれ役割を以下の表に示す。また、ここではMVCでの役割との比較も行えるよう表を作成した。


| レイヤー | Presentation Modelパターン | MVC |
| --- | --- | --- |
| Model | MVCと同様 | コマンドを受けて処理を行い、変数を更新 |
| View | Presentation Modelの変更を監視 | Modelの変更を監視し、画面の更新 | 
| Presentation Model | Modelにコマンドを送る・Modelの変更を監視・プレゼンテーションロジックやUIの状態を管理 | - | 
| Controller | Presentation Modelにコマンドを送る |  ユーザーの入力を受けて、Modelにコマンドを送る |


# 6. 画像表示アプリでMVCの実装
swift で、画像表示アプリを作成しながら、 MVC の説明を行います。

→[プログラムはこちら](https://github.com/hamadayuuki/practice-iOS-architectures/tree/MVC/MVC/MVC-sampleApp/MVC-sampleApp)

→[アプリ内で使用する画像はこちら](https://github.com/hamadayuuki/practice-iOS-architectures/tree/MVC/MVC/MVC-sampleApp/Resource)
<br>

**作成する画像表示アプリ**

<img width = 300 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/86959515-8279-7c17-2c45-0cf572c28d80.gif">

### 準備

■ ファイル構成
`Modle`:Model.swift
`View`:Cell.swift
`Controller`:ViewController.swift
<img width = 300 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/e3785948-391f-e672-5b23-73eaed5984a7.png">


■ 画像
<img width = 500 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/99c1d1d0-3f4e-e95c-2853-1c27ca0de7f1.png">
<img width = 700 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/32a119c0-22a0-262a-d716-0e40bc5e0a5b.png">

■ Storyboard
**・CollectionViewのCell の identifier を**`Cell`**にしておく**
<img width = 300 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/35657b7c-62da-91b5-8f44-032ff0c360ff.png">
<img width = 400 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/9e05df3f-d581-2b1f-8f48-b95d46814bf9.png">





### プログラム



`Model`

```Model.swift
import Foundation
import UIKit

// Viewで使用する変数の定義
// 【流れ】
// ①: ViewController.swift(Controller) からの命令で変数の値が更新されていく
// ②: 変数が更新されると View が更新される
struct Model {
    let image: UIImage!
    let imageName: String!
}
```



`View`

```Cell.swift
import UIKit

// 画面(storyboard) で使用する UIコンポーネント を定義
// 【流れ】
// ⓪: ViewController.swift が Data.swift からViewで使用するデータを取得する
// ①: ViewController.swift で　image,label(Cell.swift) の値が更新される → View が Controller を監視している状態
// ②: 画面を再描画
class Cell: UICollectionViewCell {
    
    @IBOutlet weak var image: UIImageView!
    
    @IBOutlet weak var label: UILabel!
}
```



`Controller`

```ViewController.swift
import UIKit

// Model(Model.swift)で定義した変数を使い、View(Cell.swift)を更新する
// 【流れ】
// ①: Model(Model.swift) で定義した変数を使用して、Data.swift から画像データを取得する
// ②: ①で取得したデータを、View(Cell.swift) へ伝える
class ViewController: UIViewController {

    var dataList = [Model]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Model 型のデータを受け取る
        dataList = Data.getAllData()
    }

}

// View を生成する, Model 型のデータを使用して
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
    
    // セルの個数
    // 設置必須な関数 (CollectionViewを使用する場合)
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return dataList.count
    }
    
    // セルの作成
    // 設置必須な関数 (CollectionViewを使用する場合)
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // Cell.swift の UIコンポーネント(image, label)に代入していく
        // 画像を表示する際に詰まったこと: https://qiita.com/yoshikoba/items/044925f554b75c53d27c
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
        cell.image.image = dataList[indexPath.row].image   // image の更新
        cell.image.contentMode = .scaleAspectFill
        cell.image.clipsToBounds = true
        cell.label.text = dataList[indexPath.row].imageName   // label の更新
        return cell
    }
     
}
```

# 参考文献

**[1], iOSアプリ設計パターン入門 (電子版: 税込3240円)**

https://peaks.cc/books/iOS_architecture

**[2], Swift4[Part-2] :- Model View Controller(MVC) With Example in iOS Hindi. - YouTube**

https://www.youtube.com/watch?v=bi8MyPE5vIM