Xamarin.Formsでリアクティブプログラミングを試してみる


[Reactive Extensions](http://reactivex.io/)とかいうのを使ってリアクティブプログラミングとやらに挑んでみる。

リアクティブとかよく分かってないんで正しいアプローチなのか不安。

## パッケージ追加

Reactive Extensions (Rx)[^Rx]というパッケージを追加[^NuGet]する。

こいつが追加されているとObservableのイベントハンドラをラムダ式としてSubscribeの引数に指定できるようになる。

追加しなかった場合、監視側に IObserve を実装することになり、OnNext/OnError/OnCompleted の各メソッドの実装を強制される。うざい。

![nuget.png](https://qiita-image-store.s3.amazonaws.com/0/169798/ff973605-7cef-91e8-6045-8c8d25ef7b82.png "nuget.png")


### パッケージ追加に失敗

`Could not install package 'System.Runtime.InteropServices.RuntimeInformation 4.0.0'.`

とか言われて、パッケージ追加に失敗した場合は "System.Runtime.InteropServices.RuntimeInformation" を追加したあとに再実行するとうまくいく感じ。


## コード

オブザーバーである MainPageViewModel が IObserver を実装していないことに注目。
OnNext/OnError/OnCompleted はメンバ関数ではなく、Subscribe の引数にラムダ式として指定している。

```c#:App.xaml.cs
using Xamarin.Forms;
using MyApp.Views;

namespace MyApp
{
	public partial class App : Application
	{
		public App() {
			InitializeComponent();
			MainPage = new MainPage();
		}
	}
}
```

```xml:MainPage.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
	xmlns="http://xamarin.com/schemas/2014/forms"
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	x:Class="MyApp.Views.MainPage">

	<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
		<Label Text="{Binding Greeting}" HorizontalTextAlignment="Center" />
		<Button Text="Greet" Command="{Binding GreetCommand}" />
		<Button Text="Clear" Command="{Binding ClearCommand}" />
	</StackLayout>
	
</ContentPage>
```

```c#:MainPage.xaml.cs
using Xamarin.Forms;
using MyApp.ViewModels;

namespace MyApp.Views
{
	public partial class MainPage : ContentPage
	{
		public MainPage() {
			InitializeComponent();
			BindingContext = new MainPageViewModel();
		}
	}
}
```

```c#:MainPageViewModel.cs
using System;
using System.ComponentModel;
using System.Windows.Input;
using System.Reactive.Linq;
using Xamarin.Forms;
using MyApp.Services;

namespace MyApp.ViewModels
{
	public class MainPageViewModel : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler PropertyChanged;

		public string Greeting { get; set; }

		public ICommand GreetCommand { protected set; get; }
		public ICommand ClearCommand { protected set; get; }

		public MainPageViewModel() {
			Greeting = "...";

			GreetingService service = new GreetingService();

			service.Subscribe(
				value => {
					System.Diagnostics.Debug.WriteLine("OnNext: " + value);
					Greeting = value;
					OnPropertyChanged(nameof(Greeting));
				},
				e => {
					System.Diagnostics.Debug.WriteLine("OnError: " + e.Message);
				},
				() => {
					System.Diagnostics.Debug.WriteLine("OnCompleted");
				}
			);

			GreetCommand = new Command(() => {
				service.Greet();
			});

			ClearCommand = new Command(() => {
				Greeting = "...";
				OnPropertyChanged(nameof(Greeting));
			});
		}

		protected void OnPropertyChanged(string propertyName) {
			PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}
	}
}
```

```c#:GreetingService.cs
using System;
using System.Reactive.Subjects;

namespace MyApp.Services
{
	public class GreetingService : IObservable<string>
	{
		protected Subject<string> source = new Subject<string>();

		public IDisposable Subscribe(IObserver<string> observer) {
			return source.Subscribe(observer);
		}

		public void Greet() {
			source.OnNext("こんにちわ");
		}
	}
}
```

## 結果

Greet ボタン押下で GreetingService がオブザーバーに OnNext で渡した文字列が表示されました。

![result.png](https://qiita-image-store.s3.amazonaws.com/0/169798/23b59e25-a91e-34b9-72a0-79fdf939adc1.png "result.png")


Rxの使い方ってこれで合ってるんだろうか・・・？

とりあえず以上です。


## こちらを参考にしました
http://blog.okazuki.jp/entry/20111101/1320156608
http://blog.xin9le.net/entry/2012/01/15/163827


<!-- 注釈 -->
[^Rx]: 昔は System.Reactive ではなく、Rx.NET だったらしい。
[^NuGet]: PCLだけでなく、各プラットフォーム用プロジェクトにも追加しないとダメっぽい。やはりよくわからん。