MVVMに関して

## MVCは何

[MVC](https://ja.wikipedia.org/wiki/Model_View_Controller)というアーキテクチャーは皆さんに熟知されると思います。MVC具体的にはModel、View、Controller三つの要素から構成されます。簡単に言うと、Modelはデーター層、Viewが表示層、ControllerがUIからの入力を担当します。

iOS開発ではViewControllerという存在がありまして、ViewControllerではViewとController両方の役割が持っています。それで、ViewControllerが複雑になったら、ViewController中でのロジックがどんどん増えて、FatViewControllerになります。FatViewControllerを改修することはかなり大変なことです。MVVMはその問題を解決する方法の一つ。他には色々な方法があるけど、今仕事で使ってるのはMVVMので、詳しく説明します。

## MVVMは何
[MVVM](https://ja.wikipedia.org/wiki/Model_View_ViewModel)というアーキテクチャーはModel、View、ViewModel三つの要素で構成されます。Modelはデーター層、Viewが表示層、ViewModelがUIからの入力を受けて、ビューの更新を担当します。MVCのControllerと違うのはViewModelでViewの更新はデータバインディングの手法で実現します。iOS開発では、ViewControllerに対して、ViewModelを作って、うまくデータバインディングしたら、もともとFatViewControllerでデータを処理するロジックを簡単にViewModelに移行することができます。そうしたら、ViewControllerがMVCでのVになれます。

## 開発手法

実装を書く前に、いくつのルールを説明します。

 1. **一つのViewControllerは一つのViewModelのインスタンスしか持ってません**
 2. **ViewがViewModelのインスタンスを持ってないけど、プロトコルを利用してデーターバインディングします**
 3. **実際の実装ではModelがViewModelに含まれるケースもあります**

早速実装手法に関して説明します。データーバインディングはReactiveSwiftというライブラリを使ってやります。

```swift
class ViewModel {
	//データーバインディング必要な変数
	let paramA = MutableProperty<Int>(0)
	init(data: [AnyHashable: Any]) {
		if let a = data["paramA"] as? Int {
			paramA.swap(a)
		}
	}
}

class ViewController: UIViewController {
	private let viewModel: ViewModel
	
	init(data: [AnyHashable: Any]) {
		viewModel = ViewModel(data: data)
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
		bind(viewModel)
	}
	
	private func bind(_ viewModel: ViewModel) {
		//データーバインディング
		viewModel.paramA.take(during: reactive.lifetime).observeValue { (value) in
			//paramAが変わったら、この変数を使ってるViewが自動的に更新されます。
		}
	}
}
```

ViewController初期化する時のデーターを利用してViewModelを初期化します。その後ViewDidLoadでViewModelとViewControllerをバインディングします。

ViewModelのparamAの数値が変わったら、ViewControllerでは自動的にその変化を反映します。

次は子Viewを追加して

```swift
protocol AViewModel {
	var paramB: MutableProperty<String> { get }
}
class AView: UIView {
	func bind(_ viewModel: AViewModel) {
		viewModel.paramB.take(during: reactive.lifetime).observeValue{ (value) in
			//paramBを使って、AViewを更新します。
		}	
	}
}
```

まずAViewというViewを定義して、そしてProtocol型のViewModelを声明します。ここのViewModelがクラス型ではなくProtocol型で声明する理由は後で説明します。続いてはViewModelの修正です。


```swift
class ViewModel: AViewModel {
	let paramA = MutableProperty<Int>(0)
	let paramB = MutableProperty<String>("")
	
	init(data: [AnyHashable: Any]) {
		if let a = data["paramA"] as? Int {
			paramA.swap(a)	
		}
		if let b = data["paramB"] as? String {
			paramA.swap(b)	
		}
	}
}
```

ViewModelをAViewModelのプロトコルを実装して、ViewControllerにAView型の子Viewを追加して、同時にbind関数を修正します。


```swift
class ViewController: UIViewController {
    private let viewModel: ViewModel
	let aView = AView()
	
	init(data: [AnyHashable: Any]) {
		viewModel = ViewModel(data: data)
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
		view.addSubView(aView)
		bind(viewModel)
	}
	
	private func bind(_ viewModel: ViewModel) {
		//子Viewのデーターバインディング
		aView.bind(viewModel)
		//データーバインディング
		viewModel.paramA.take(during: reactive.lifetime).observeValue { (value) in
			//paramAが変わったら、この変数を使ってるViewを更新します。
		}
	}
}

```
こうすれば、子ViewとViewModelをデーターバインディングができます。なぜ子ViewのViewModelがクラス型ではなくプロトコル型ですか？このような実装は必ず正しいとは言えませんが、そうする三つの理由を説明します。

1. **一つのViewControllerに対してデーター処理のロジックはできるだけ一箇所にまとめます**
もし子ViewのViewModelがデーターを処理するロジックを持っていれば、子Viewと子Viewの間にデーターを更新したい時、その実装はちょっと面倒になります。ロジックが一箇所にまとめたら簡単にできます。でもFatViewModel問題も発生かもしれない。
2. **子ViewのViewModelがProtocol型にしたら、再利用は簡単です**
再利用の場合、この子Viewを持ってるViewControllerのViewModelを子ViewModelを実装すれば完了。Interface向けの考え方はOOP、SwiftではPOP(Protocol Oriented Programing)でもおすすめられます。
3. **メモリー管理がわかりやすくなる**
データーバインディンに対して、うまく書かないとメモリーリークの恐れがあります＊（ViewModelとかViewなど生きたままViewControllerも釈放できません）。一つのViewModelになると、ViewModelのデーターに対して観察の動作をちゃんと終われば、ViewModelを釈放できます。

初めてのqiita投稿、m(..)mよろしくお願いします。