Xamarin.FormsのMVVM

# Xamarin.FormsのMVVM
[Xamarin Advent Calendar 2016 (その１)](http://qiita.com/advent-calendar/2016/xamarin) 3日目(12月3日)です。現在アメリカにいて日付の感覚が合っていないかもしれませんが、こちらはまだ1日前(12月2日)です。(言い訳w)

半年ほどXamarin.Formsで開発してみました。ほぼ、業務アプリ要件であれば何でもできるようになりました。できないことは多分ないです。共通化・生産性を考えれば、これ以上はないほど気に入りました。
アドベントカレンダーネタとして何を書こうか考えていたのですが、海外にいてあまり時間がなかったので、Xamarin.FormsのMVVM周りの話を超かんたんレベルで書いてみます。
色々試して、実用レベルのアプリを作った経験でのお話です。個人の好みが入りますが、ご了承ください。
## MVVM Framewrok for Xamarin
Xamarin向けのMVVM Frameworkって結構色々あって皆さん悩んでいるんじゃないでしょうか。

+ Prism
+ ReactiveUI
+ MvvmCross
+ ReactiveProperty

などなどが有名どころですか。
それぞれ、ある程度試したり、実装見てやってみました。
最終的に落ち着いたところは、**何も使わない**です。
意外と知られていないかもしれませんが、Xamarin.Forms自体がMVVMのサポートをほぼ満たしてくれるんですね。
### MVVM Framewrokを使うメリット
+ 使い方によっては生産性上がり便利
+ MVVM以外の非機能要件で使えるものもある(RX, i18n, 非同期など)
+ 実装方針がある程度決まる

### MVVM Framewrokを使うデメリット
+ モバイルアプリのサイズがでかくなる
+ 他のライブラリーとの依存関係が合わない、バージョン依存
+ 最新アップデートにすぐ対応されないことも
+ 作り方に制約ができる、自分の理想と違うコードになったり
+ 機能がtoo much、モバイル開発なんで使わない機能なんか入れたくない

今回は、デメリットの方が遥かにでかかったのと、実際に使ってみてパッとしなかったので**何も使わない**方針としました。
結果、全く問題なかったです。性能良く、バイナリーサイズの小さいアプリができました。
実装レイヤーを増やして複雑になるよりは、シンプルなMVVM階層とし、ステップ数を減らす努力をしたほうが良いです。(個人的に)
例えば、ReactivePropertyなんかはPrimitiveなデータ構造捨てて、超Framework密結合な作りになってしまいますね。個人的にPrimitiveかつOpenで汎用性の高い作りが好きです。もちろんReactiveでAsynchronousなコードを書きたい時は、RXのみ導入するのは全然ありです。

## 生Xamarin.FormsのMVVM階層
1. **Model** VMのラッパーか、データソースか
2. **View** 絶対にXamlで書いた方がいいです。ConverterやFormatterは標準の機能でほぼ足ります
3. **ViewModel** WebAPIとSerializableであればModelと一緒の1階層でもOKに思います。余計なDTOを使うのはナンセンス(Framewrokとしても)です

基本データバインディングなので、Xamlを駆使してViewModelの見せ方を変えます。
ここまでのところ、Xamarin.Formsの持つSDKの範囲内で問題なく実装できます。問題は、WPFやWinFormsでもお馴染みのINotifiyPropertyChangedの実装をどうするかという点です。
昔からこの問題に対しては色々な対策が考えらています。
10年以上C#開発やってきて、現時点でベストだと思う方法があります。以下Fodyを使います。

## Fody.PropertyChanged
[Fody](https://github.com/Fody/Fody)はILのアセンブリ操作ライブラリーです。
公式によると、

> Manipulating the IL of an assembly as part of a build requires a significant amount of plumbing code. This plumbing code involves knowledge of both the MSBuild and Visual Studio APIs. Fody attempts to eliminate that plumbing code through an extensible add-in model.

ということだそうです。
その中で有名な拡張として[Fody.PropertyChanged](https://github.com/Fody/PropertyChanged)があります。要は、ビルド時に**いい感じにしてくれる**ってことです。

### 普通のINotifyPropertyChanged実装
[Xamariの公式ヘルプ](https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/)からINotifyPropertyChangedのコードを引用します。

```
using System;
using System.ComponentModel;
using Xamarin.Forms;

namespace XamlSamples
{
    class ClockViewModel : INotifyPropertyChanged
    {
        DateTime dateTime;

        public event PropertyChangedEventHandler PropertyChanged;

        public ClockViewModel()
        {
            this.DateTime = DateTime.Now;

            Device.StartTimer(TimeSpan.FromSeconds(1), () =>
                {
                    this.DateTime = DateTime.Now;
                    return true;
                });
        }

        public DateTime DateTime
        {
            set
            {
                if (dateTime != value)
                {
                    dateTime = value;

                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this,
                            new PropertyChangedEventArgs("DateTime"));
                    }
                }
            }
            get
            {
                return dateTime;
            }
        }
    }
}
```
何というクソコードでしょう。実践でこんなクソコード見たことがありません。	

### Fody.PropertyChangedを使った実装
それでは、Fody.PropertyChangedを使った場合はどうでしょう。これも公式からそのまま引用したので、クラスやPropertyが違いますが気にしないでください。

```
[ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}
```
何ということでしょう。PropertyChangedの処理が一切無くなって、**PrimitiveかつOpen**で汎用性の高いデータクラスです。
唯一の実装は、以下のAttributesのみです。
```
[ImplementPropertyChanged]
```

### Fody.PropertyChangedを使った実装をデコンパイルした中身
それではコンパイルされたアセンブリー(DLL)の中身はどうなっているでしょうか。適当に[dotPeek](https://www.jetbrains.com/decompiler/?fromMenu)などのデコンパイラーで確認できます。

```
public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }

    string familyName;
    public string FamilyName
    {
        get { return familyName; }
        set 
        {
            if (value != familyName)
            {
                familyName = value;
                OnPropertyChanged("FamilyName");
                OnPropertyChanged("FullName");
            }
        }
    }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }

    public virtual void OnPropertyChanged(string propertyName)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
```
実装は、ちゃんとpropertyChangedに展開されています。そして注目は、**FullName**に対して、**GivenNames**と**FamilyName**のsetterでOnPropertyChangedが呼ばれています。有能ですね。

## まとめ
Xamarin.Formsは、それ自身がMVVMの機能を持っています。Frameworkで迷うぐらいであれば使わないという選択肢もありです。
要件に合わせ、SimpleでLightweightなモバイルアプリを作りましょう。Framework非依存なコードにしておけば後から拡張も容易です。