MVCについてコードを記載してアウトプットしてみた

## はじめに
MVCについてアウトプットしてみる。
また、アウトプットする上でNtificationCenterという通知方法を使用するが、本来の使い方ではない。そのため、あくまでMVCの学習の一環としてNtificationCenterを使用させていただきたい。

## MVCの各々機能について
- Model
    - システム内のビジネスロジックを担当する。
        - UIに関係しない処理全て
- View
    - ユーザーが直接目にするView部分の描画処理を行う
- Controller
    - ModelとViewの仲介役（橋渡し的役割）
    - ユーザーから入力された情報をModelに伝え、
    - Modelが受け取った情報をModelが更新したら、このControllerを介してViewへ描画を指示する

## ソースコード
- Model
```swift:Swift
import Foundation
import UIKit

//Model
//ビジネスロジック、UIに関係しない処理全て

//Modelがやること
//①データを表示する処理
//②テーブルセル押すと件数が増える処理
//※セルの個数を確定する処理（UITableViewのUITableViewDatasourceのcellForRowAt）はController（ViewとModelの仲介）で担当する責務ではないため、Modelで対応する


//ツイート自体のデータモデル
class TweetDataModel {
    let tweet: String
    
    init(tweet: String) {
        self.tweet = tweet
    }
}

class TweetListModel: NSObject, UITableViewDataSource {
    //Modelを監視するクラス
    let notificationCenter = NotificationCenter()
    
    //Modelで管理する配列に初期値を設定する
    var tweetList: [TweetDataModel] = [
        TweetDataModel.init(tweet: "Tweet: 0番目"),
        TweetDataModel.init(tweet: "Tweet: 1番目"),
        TweetDataModel.init(tweet: "Tweet: 2番目")
    ] {
        didSet{
            //Modelで管理している配列に変化があった場合に呼び出されて通知する
            //userInfoはnotificarionCenterにpostした時に値を受け渡すことができる、今回はtweetListの値を受け渡す
            //通知でViewControllerに変更を知らせる（配列の内容が変更時）
            notificationCenter.post(name: .init("changeTweetList"), object: nil, userInfo: ["list": tweetList])
        }
    }
    
    //配列に新しいツイートを追加する
    func addTweetList() {
        let tweetText = "Tweet: \(self.tweetList.count)番目"
        self.tweetList.append(TweetDataModel.init(tweet: tweetText))
    }
    
    //MARK: UITableViewDatasource
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.tweetList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        let tweetModel = self.tweetList[indexPath.row]
        cell.textLabel?.text = tweetModel.tweet
        return cell
    }
}
```
- View
```swift:Swift
import UIKit

//View

//Viewがやること
//ユーザーが直接目にするView部分の描画処理を行う
//UIViewクラスを作成し、xibファイルと紐付ける（UIViewControllerでcibファイルを持たない）

class TweetListView: UIView {
    @IBOutlet weak var tableView: UITableView!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
//        initメソッドはinitメソッド内で定数や配列の初期化などを行うためにある
        loadNib()
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        loadNib()
    }
    
    func loadNib() {
        let view = Bundle.main.loadNibNamed("TweetListView", owner: self, options: nil)?.first as! UIView
        view.frame = self.bounds
        self.addSubview(view)
    }
}
```
- Controller
```swift:Swift
import UIKit

//Controller
//ViewControllerでTableViewのデリゲートメソッドを宣言している

//Controllerがやること
//①Viewでのユーザーアクションを検知して、Modelに処理を走らせる
//②Modelの値の変化を受けて、Viewへ反映させる

class TweetViewController: UIViewController {
    
    //ModelのTweetListModel
    var myModel: TweetListModel? {
        //セットされるたびにdidSetが動作する
        didSet {
            //ViewとModelとを結合し、Modelの監視を開始する
            registerModel()
        }
    }
    
    override func loadView() {
        super.loadView()
        self.view = TweetListView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        myModel = TweetListModel()
        settingTableView()

    }
    
    func settingTableView() {
        let tweetListView = self.view as! TweetListView
        tweetListView.tableView.delegate = self
        tweetListView.tableView.dataSource = self.myModel
        
        //TableViewに表示するCellを登録する
        tweetListView.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    }
    
    //viewとModel監視する関数
    func registerModel() {
        guard let model = myModel else { return }
        
        //Modelにある配列が変化したらnotificationcenterでModelから通知を受け取ってaddObserverのクロージャー以降の処理を実行させる(今回でいう配列が変化したら、ここのviewにTweetListViewを作成し、TweetListViewのtableviewをリロードさせたい)
        model.notificationCenter.addObserver(forName: .init("changeTweetList"), object: nil, queue: nil) { [unowned self] notification in
            //Modelで更新した内容をViewへ反映（更新）
            let tweetListView = self.view as! TweetListView
            tweetListView.tableView.reloadData()
        }
    }
    
    @objc func onTapTableViewCell() {
        myModel?.addTweetList()
    }
}

//Viewでのユーザーアクションを検知して、Modelに処理を走らせる
extension TweetViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //Modelでタップされたときに追加処理を行う（Modelに処理を走らせる）
        self.onTapTableViewCell()
    }
}
```

## おわりに
間違い等ありましたらコメント欄にてご指摘いただけると幸いです。

## 参考記事
- [【Swift】MVCについて分かりやすいサンプルが無かったのでSwiftでMVCパターンで簡易アプリ書いてみた【MVCパターン】](https://zenn.dev/tanukidevelop/articles/33eedf516abb1e)

- [[増補改訂第3版]Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plusシリーズ)　](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%AC%AC3%E7%89%88-Swift%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%E7%9B%B4%E6%84%9F%E7%9A%84%E3%81%AA%E6%96%87%E6%B3%95%E3%81%A8%E5%AE%89%E5%85%A8%E6%80%A7%E3%82%92%E5%85%BC%E3%81%AD%E5%82%99%E3%81%88%E3%81%9F%E8%A8%80%E8%AA%9E-PRESS-plus%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA/dp/4297112132)
↑引用元
石川 洋資 (著), 西山 勇世 (著)
発行者：片岡 巌
出版社：技術評論社
印刷/製本：日経印刷株式会社

## 開発環境
- Xcode-13.4.1
- Swift version 5.7