MvvmCrossを触る前に

MvvmCrossの日本語資料が少ないので、理解力向上の目的で自分でまとめて作ってみようと思います。
※注意：全然メジャーなフレームワークじゃないので、間違っても飛びつかないようにしてください（いいなあと思って、プラグインのOSSをコミットしてくれる方が増えれば嬉しいです・・）

# 想定読者

* MVVMアーキテクチャパターンは理解してる
  * 担当しているXamarinプロジェクトにMVVMCrossが採用されているけど、どのように使用すればいいかわからない
  * MVVMアーキテクチャのフレームワークを作りたい方の参考程度

# MvvmCrossとは

Xamarinのクロスプラットフォーム開発のためのフレームワークです。
MVVMアーキテクチャパターンをクロスプラットフォーム開発に応用することで**プラットフォーム固有のコードの領域を最小限に抑える**ことができるようになり、**開発工数の削減、保守性の向上**を狙うことができます。
MVVMアーキテクチャをクロスプラットフォーム開発で導入するには多くの壁があるのですが、MVVMCrossで用意されている仕組みを使うことで解決できるようになっています。

## 採用するかどうかについて

どうやら、Xamarin.Formsができる前あたりに注目されたフレームワークらしいです。
なので会社で無闇に採用するメリットはほとんどないと思います。

強いて言うなら、

* C#/.NETがバリバリできる
* ゆくゆくはAndroid/iOSのネイティブでも開発できるようにしなければならない
* 開発工数の削減を強いられている

時にもってこいのフレームワークだと思います。

そうでなければXamarin.Forms + Prismで良いと思います。
（Xamarin.FormsとPrismは使ったことがありませんが、Xamarin.Formsの利点を考えるとMVVMCrossの利用価値が被ってしまいますし、Prismの方がメジャーなので情報が多いからです。）

# サポートしているプラットフォーム一覧

* Xamarin.iOS
* Xamarin.Android
* Xamarin.Mac
* Xamarin.Forms
* UWP
* WPF

# MVVMCrossの特徴
## MVVMアーキテクチャパターンを採用

MVVMで開発したい場合にはもってこいです。
MVVMアーキテクチャのメリットについて詳細で話すことはここではしません。

### クロスプラットフォーム開発におけるMVVMの課題

MVVMのViewは通常、画面の向きが変わったときの見た目を分けたり、ユーザーの属性ごとに提示するUIを分けたりするために一つのViewModelに対して複数のViewを持つことができます。
また、ViewModelはViewに依存しないため、ViewModelはViewどんな状態であるかを知る必要がありません。そのため、UIの変更に強いアーキテクチャとなります。

一方、クロスプラットフォーム開発におけるMVVMのViewは、上記に加えて一つのViewModelに対して各プラットフォームごとの画面を構成するViewを複数持てるようにする必要があります。

このときクロスプラットフォーム開発におけるMVVMでは次の制約を満たす必要が出てきます。

```
ViewModelにはプラットフォーム固有のコードを書いてはいけない
```

この制約を満たすためには固有のコードはViewに書かなくてはならなくなります。
さて、この状態で２つ以上のViewModelが作成されたときどのような不都合が生じるでしょうか。

<img width="353" alt="fig" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202772/9bd737a4-f55b-5484-aed2-72d4ee284092.png">


View(Android)にあるAndroid固有のコードとHogeView(Android)にあるAndroid固有のコードに同じ処理があってそれをすべて変更したいという場合すべてのViewでそれを書き換えなければならなくなります。

そこでMVVMCrossではアプリケーションに共通する機能をすべてフレームワークの中に隠蔽し、それらの処理についてはViewModel内で記述できるような仕組みを提供しています。例えば、`MvxViewModel`を継承してナビゲーションを行うなどがあります。

<img width="572" alt="fig" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202772/75ac2a01-d65e-67d5-469e-b392a59cd2cc.png">

OSごとにUtilクラスなどを作ってしまえばいいのではないかという疑問が浮かばれると思いますが、参照関係が複雑になりテスタビリティやメンテナンス性が落ちてしまう原因になるのでおすすめできません（詳しくはDependency Injectionで調べてみてください）。

そういったテスタビリティとメンテナンス性を最大限に発揮できる状態を維持してくれる手助け役としてMVVMCrossがあります。
最初によくありそうな誤解として「MVVMアーキテクチャはこれがないとできない」というものがあると思いますが、実際は自分で作ろうと思えば作ることはできます。

それがかなり面倒なのでこのフレームワークの機能を組み合わせて作っていきましょうというコンセプトです。OSSコミッターさんありがとう。

## ナビゲーションシステム (Navigation System)

Androidで画面遷移する場合は`StartActivity`を使用したり、iOSで画面遷移する場合は`Segue`で指定したりなど、画面遷移の仕組みは各プラットフォーム間で異なります。
そこで、先程の課題でも説明したとおり、各プラットフォーム間で異なる画面遷移の仕組み([Navigation](https://www.mvvmcross.com/documentation/fundamentals/navigation))をMvvmCrossは強力にサポートしています。

## データバインディング (Data Binding)

MVVMではViewModelはViewの内容を知ることはできません。御存知の通りViewModelはViewの変更をViewから登録されたデータバインディングによって自動的に通知されることで知ることができます。
ですが、先程も説明したようにViewはプラットフォームによって様々です。AndroidだとXML形式でUIを記述したり、iOSだとStoryboardやxibで記述したりします。
Storyboardの生データは非常に扱いづらく、同じようなバインディングの仕組みを提供することが困難であったりします。そこで、MVVMCrossでは[複数のデータバインディングの方法](https://www.mvvmcross.com/documentation/fundamentals/data-binding#mvvmcross-data-binding)を提供しています。

はじめはなぜかSwiss記法やFluent記法など複数の記法があって混乱すると思います。これらは**サポートしているプラットフォームの全てのViewでデータバインディングができるように用意されている**と考えれば問題ありません。

## プラットフォーム固有のサポート (Platform specifies support)

AndroidではViewというとActivityやFragment、Dialogなど複数の表現方法があります。iOSにもViewControllerやViewなど複数の表現方法があります。
各Viewに対してViewModelを対応付けられるように、MVVMCrossでは、それぞれのコンポーネントに対してのサポートが充実しています。

また、ViewModelになるべく多くの処理を移譲できるように、繰り返しなどのビューロジックもViewModelから記述できるようなサポートが用意されています。
（ただし、ここらへんはドキュメントが少なすぎるのでソースを直接見ることになります。。）

## 制御の反転と依存性の注入エンジン ( Inversion of Control container and Dependency Injection engine )

言わずもがな、DIを実現できるようにするための仕組みが提供されています。また、`MvxViewModel`が標準でコンストラクタによるDIを装備していたり、様々な部分で便利に扱うことができます。
このおかげで、ViewModelのテスタビリティが保証されています。

## 共通の機能に対するたくさんのプラグイン ( Lots of plugins for common functionalies )

MvvmCrossが提供しているプラグインという仕組みを使うことで、ViewModelに直接システム固有のロジックを記述することができるようになります。また、自作プラグインを作るための仕組みも提供されています。

## 単体テストヘルパー ( Unit Test Helpers )

特にViewModelの単体テストなどをテストする時の仕組みがすでに用意されています。

# 参考
* https://www.mvvmcross.com/documentation/getting-started/getting-started