Xamarin で
ReactiveUI を使ってみた
2014.4.19
すまべん特別編「Xamarin 2.0であそぼう!」@関東
1
自己紹介
• @amay077
• 位置情報エンジニア、

モバイルアプリエンジニア、etc
• VB6→VC6→C#2.0

→Java/Obj-C→C#5.0
• Cosmoroot,Inc(Nagoya,
Tokyo)
2
地図, 位置情報,
オープンデータ,
C#, Android, iOS,
Xamarin
Now Hot topics
•        (ロジネビュラ)
• 千年先まで費用ゼロのクラウド型倉庫管理システム
•       (ジクウ)
• リアルタイム位置データ収集プラットフォーム
• Nepula(ネプラ)
• 基幹業務システム向けPaaS
おもにココ担当
クラウドサービスプロバイダ
3
HexRinger developed at
ハマッカソン #2
ref http://blog.airs.co.jp/2010/12/06/hamackathon-20101204.html
4
しゃべPOI developed for
OpenStreetMappers
続きは… 位置情報系Androidアプリケーションの開発 - Togetterまとめ
5
富士フォト with ふじのくにオープンデータ
MashupAward9, アーバンデータチャレンジ東京2013
ref シビックハックの広まりと地方エンジニアの躍進 ‒ MA9総括 ¦ finder
6
富士フォト with ふじのくにオープンデータ
MashupAward9, アーバンデータチャレンジ東京2013
7
iOS版は Xamarin で
作りました
おことわり
• 資料内の C# コードはスペース節約のため「後
ろ { カッコ」にしています
• Xamarin の勉強会なので iOS/Android 中心の
話をします
• Mac が主PC なので Xamarin Studio を使って
います
8
目次
• MvvmCross ではダメ?
• Reactive Extensions について
• ReactiveUI とは
• まとめ
9
MvvmCross
ViewModel Model
View(Droid) View(iOS)
IHogeService
HogeDroid HogeTouch
PCL
PF毎のView+DataBinding
PF固有機能(カメラ、アプリ連
携、GPS…)
10
MvvmCross
• Xamarin でクロスプラットフォームアプリを開
発する際の事実上標準のMVVMフレームワーク
• Android, iOS, WinStore, WP, Mac などに対応
• M-VM を PCL で共通にできる
• プラットフォーム依存機能は IoCコンテナで
11
これで良くね?
12
MvvmCross のデメリット
• 全方位&高機能が故にFat!
• アセンブリ数:15(プラグインも入れると
40over)
• チュートリアル動画が40本以上もある
• ルールたくさん(Binding、ValueConverter、
Service、Plugin…)
13
MvvmCross のデメリット
• クロスプラットフォームな為、機能が最大公約数
• 前の画面からの戻り値を受け取れない(Android
の onActivityResult)とか
• 各PFの最新機能に追いつけない
• iOS - Storyboard
• Android - Fragment
14
MvvmCross 以外の
選択肢
15
QuickCross
• https://github.com/MacawNL/QuickCross
• No-Binary!
• PowerShell でコマンド叩くとコードを生成
• Mac なので試せませんでした…
16
propertycross.com
各SDKでクロスPF開発するための指針が書いてあるみたい
圧倒的じゃないか…
17
そして ReactiveUI
18
…の前に
!
Reactive Extensions
!
について
19
Reactive Extensionsとは
• https://rx.codeplex.com/
• 非同期処理やイベントを LINQ っぽく書いてチェ
インできるスゴイやつ
• TPL(async/await含む) と比べると、複数の結果
を返せるのでストリーム処理に便利
20
私とRx
• Android の非同期処理でコールバック地獄
• jQuery.Deferred みたいなの欲しい
• 「.NET には Rx がある」というのを知る

ref:Reactive Extensionsで非同期処理を簡単に by @neueccさん

「なにこれスゴイ!」
• Java には Rx ないの?
21
Java でも Rx
• reactive4java - 開発終了
• Androidアプリ開発で使ってます
• LINQ to Objects もあるよ
• RxJava
• 事実上標準の Rx for Java になる気配
• Netflix なので安心なんじゃないでしょうか
22
こうなる事は分かってた
// Java - reactive4java!
ObservableBuilder.range(0, 9)!
.where(new Func1<Integer, Boolean>() {!
@Override public Boolean invoke(Integer x) {!
return x % 2 == 0;!
}!
}).select(new Func1<Integer, Float>() {!
@Override public Float invoke(Integer x) {!
return x / 2f;!
}!
}).register(new Observer<Float>() {!
@Override public void next(Float x) { !
System.out.println(x); !
}!
@Override public void finish() { }!
@Override public void error(Throwable arg0) { }!
});
23
// C#!
Observable.Range(0, 10)!
.Where(x => x % 2 == 0)!
.Select(x => x / 2f)!
.Subscribe(Console.WriteLine);
で ReactiveUI とは
24
ReactiveUI
• http://www.reactiveui.net/
• Reactive Extensions を全面的に採用した
MVVMフレームワーク
• WPF, WP, WinStore, iOS, Android, Mac 対応
• 元Microsoft で GitHub の中の人が作ってる
25
MvvmCross のこれ…
ViewModel Model
View(Droid) View(iOS)
IHogeService
HogeDroid HogeTouch
PCL
26
ReactiveUI でもできます
ViewModel Model
View(Droid) View(iOS)
IHogeService
HogeDroid HogeTouch
PCL
IObservable
IObservable
IObservable
M-V-VM の「つなぎ」に
Rx を使ったら…
…もっと COOL に!
27
基本的なクラス構成
MvxViewModel
: INotifyPropertyChanged
MvxActivity
MvxViewController
…
MvvmCross
ReactiveObject
: INotifyPropertyChanged
ReactiveActivity
ReactiveViewController
…
ReativeUI
View ViewModel
28
ViewModel の例
public class HogeViewModel : ReactiveObject {!
string _myName;!
public string MyName {!
get { return _myName; }!
set { this.RaiseAndSetIfChanged(ref _myName, value); }!
}!
!
public HogeViewModel() {!
this.MyName = "amay077";!
}!
}
29
View と Binding の例
public partial class HogeViewController : !
ReactiveViewController, IViewFor<HogeViewModel> {!
public override void ViewDidLoad() {!
base.ViewDidLoad();!
!
this.OneWayBind(this.ViewModel, !
vm => vm.MyName, !
v => v.lblMyName.Text, !
x => x.ToUpper());!
!
this.ViewModel = new HogeViewModel();!
}!
}
30
View と Binding の例
この ViewModel の…
MyName プロパティを…
Viewのラベルに表示する…
…大文字に変換してからね
31
public partial class HogeViewController : !
ReactiveViewController, IViewFor<HogeViewModel> {!
public override void ViewDidLoad() {!
base.ViewDidLoad();!
!
this.OneWayBind(this.ViewModel, !
vm => vm.MyName, !
v => v.lblMyName.Text, !
x => x.ToUpper());!
!
this.ViewModel = new HogeViewModel();!
}!
}
Binding いろいろ
// this = ReactiveViewController!
!
// 双方向 Binding!
this.Bind(this.ViewModel, !
vm=> vm.MyName, v => v.EditMyText.Text);!
!
// コマンドに Bind!
this.BindCommand(this.ViewModel, !
vm => vm.MyCommand, !
v => v.MyButton);
32
Rx っぽい ViewModel
public class HogeViewModel : ReactiveObject {!
ObservableAsPropertyHelper<DateTime> _time;!
public DateTime Time {!
get { return _time.Value; }!
}!
!
public HogeViewModel() {!
_time = Observable.Interval(!
TimeSpan.FromSeconds(1))!
.Select(x => DateTime.Now)!
.ToProperty(this, vm => vm.Time);!
}!
}
33
public class HogeViewModel : ReactiveObject {!
ObservableAsPropertyHelper<DateTime> _time;!
public DateTime Time {!
get { return _time.Value; }!
}!
!
public HogeViewModel() {!
_time = Observable.Interval(!
TimeSpan.FromSeconds(1))!
.Select(x => DateTime.Now)!
.ToProperty(this, vm => vm.Time);!
}!
}
Rx っぽい ViewModel
1秒毎に…
現在時間を…
プロパティに設定する
34
Property を IObservable へ
public class HogeViewModel : ReactiveObject {!
bool _isAgreed;!
public bool IsAgreed {!
get { return _isAgreed; }!
set { this.RaiseAndSetIfChanged(ref _isAgreed, value); }!
}!
!
public HogeViewModel() {!
IObservable<bool> agreed = !
this.ObservableForProperty(vm => vm.IsAgreed)!
.Select(x => x.Value);!
}!
}
35
Reactive な Command
public class HogeViewModel : ReactiveObject {!
public bool IsAgreed { // 省略!
public ICommand Register { get; private set; }!
!
public HogeViewModel() {!
IObservable<bool> agreed = !
this.ObservableForProperty(vm => vm.IsAgreed)!
.Select(x => x.Value);!
var cmd = new ReactiveCommand(agreed);!
cmd.Subscribe(_ => /* 実行した時の処理 */);!
this.Register = cmd;!
}!
}
36
View で使う
public partial class HogeViewController : !
ReactiveViewController, IViewFor<HogeViewModel> {!
public override void ViewDidLoad() {!
!
this.Bind(this.ViewModel, !
vm=> vm.IsAgreed, v => v.ToggleAgreed);!
!
this.BindCommand(this.ViewModel, !
vm=> vm.Register, v => v.RegisterButton);
37
Registerコマンドの CanExecute が true の時のみ押せる
実行したところ
38
vm.Register.Subscribe(
_=> /* 実行した時の処理 */)
Command実行∼
Property更新を
流れるように
39
シナリオ
40
Model
!
.GpsAvailable
.GetLocations()
ViewModel
!
.Location
.StartGps()
View
bind
notify
listen
notify
Model(適当)
public class MyLocationModel {!
public IObservable<bool> GpsAvailable() {!
return Observable.Return(true);!
}!
!
public IObservable<LatLon> GetLocation() {!
var r = new System.Random();!
!
return Observable.Interval(TimeSpan.FromSeconds(1))!
.Select(x => new LatLon(!
34d + r.NextDouble(), !
135d + r.NextDouble()));!
}!
}
41
ViewModel
public class FourthViewModel : ReactiveObject {!
MyLocationModel _model = new MyLocationModel();!
!
ObservableAsPropertyHelper<LatLon> _location;!
public LatLon Location { get { return _location.Value; } }!
!
public ICommand StartGps { get; private set; }!
!
public FourthViewModel() {!
var cmd = new ReactiveCommand(_model.GpsAvailable());!
!
_location = cmd.SelectMany(_ => _model.GetLocation())!
.ToProperty(this, vm => vm.Location);!
!
this.StartGps = cmd;!
}!
}
42
public class FourthViewModel : ReactiveObject {!
MyLocationModel _model = new MyLocationModel();!
!
ObservableAsPropertyHelper<LatLon> _location;!
public LatLon Location { get { return _location.Value; } }!
!
public ICommand StartGps { get; private set; }!
!
public FourthViewModel() {!
var cmd = new ReactiveCommand(_model.GpsAvailable());!
!
_location = cmd.SelectMany(_ => _model.GetLocation())!
.ToProperty(this, vm => vm.Location);!
!
this.StartGps = cmd;!
}!
}
ViewModel
43
Execute されたらモデルで測位開始、
結果を逐次 Property へ
GPS が使えるなら
CanExecute = true
View は普通に Binding
public override void ViewDidLoad() {!
base.ViewDidLoad();!
!
this.OneWayBind(this.ViewModel, !
vm => vm.Location, v => v.LatLabel.Text, !
x => x.Lat.ToString("0.00"));!
!
this.OneWayBind(this.ViewModel, !
vm => vm.Location, v => v.LonLabel.Text, !
x => x.Lon.ToString("0.00"));!
!
this.BindCommand(this.ViewModel, !
vm => vm.StartGps, v => v.StartGpsButton);!
!
this.ViewModel = new FourthViewModel(); !
}
44
複合条件
(Rx を活かして)
45
シナリオ
46
ViewModel
!
.Red
.Green
.Blue
.Color
View
bind
notify
depends
ViewModel
public class FifthViewModel : ReactiveObject {!
float _red;!
public float Red {!
get { return _red; } set { this.RaiseAndSetIfChanged(ref _red, value); }!
}!
/* Blue, Green は省略 */!
!
ObservableAsPropertyHelper<Color> _color;!
public Color Color { get { return _color.Value; } }!
!
public FifthViewModel() {!
var r = this.ObservableForProperty(vm => vm.Red).Select(x => x.Value);!
var g = this.ObservableForProperty(vm => vm.Green).Select(x => x.Value);!
var b = this.ObservableForProperty(vm => vm.Blue).Select(x => x.Value);!
!
_color = Observable.CombineLatest(r, g, b, !
(x, y, z) => new Color(x, y, z)).ToProperty(this, vm => vm.Color);!
}!
}
47
public class FifthViewModel : ReactiveObject {!
float _red;!
public float Red {!
get { return _red; } set { this.RaiseAndSetIfChanged(ref _red, value); }!
}!
/* Blue, Green は省略 */!
!
ObservableAsPropertyHelper<Color> _color;!
public Color Color { get { return _color.Value; } }!
!
public FifthViewModel() {!
var r = this.ObservableForProperty(vm => vm.Red).Select(x => x.Value);!
var g = this.ObservableForProperty(vm => vm.Green).Select(x => x.Value);!
var b = this.ObservableForProperty(vm => vm.Blue).Select(x => x.Value);!
!
_color = Observable.CombineLatest(r, g, b, !
(x, y, z) => new Color(x, y, z)).ToProperty(this, vm => vm.Color);!
}!
}
ViewModel
48
3つの値が ったら初回設定、
以降はいずれかが変わったら設定
View と Binding
public override void ViewDidLoad() {!
this.Bind(this.ViewModel, vm => vm.Red, v => v.RedSlider.Value);!
this.Bind(this.ViewModel, vm => vm.Green, v => v.GreenSlider.Value);!
this.Bind(this.ViewModel, vm => vm.Blue, v => v.BlueSlider.Value);!
!
this.OneWayBind(this.ViewModel, !
vm => vm.Color, !
v => v.ColorView.BackgroundColor, !
x => new UIColor(x.R, x.G, x.B, 1f));!
}
49
iOSなのでUIColorに変換
View で MultiBinding でも
public override void ViewDidLoad() {!
var r = this.ObservableForProperty(v => v.ViewModel.Red)!
.Select(x => x.Value);!
var g = this.ObservableForProperty(v => v.ViewModel.Green)!
.Select(x => x.Value);!
var b = this.ObservableForProperty(v => v.ViewModel.Blue)!
.Select(x => x.Value);!
!
Observable.CombineLatest(r, g, b, !
(x, y, z) => new UIColor(x, y, z, 1f))!
.ObserveOn(RxApp.MainThreadScheduler)!
.Subscribe(x => this.ColorView.BackgroundColor = x);!
}
50
UIThread で
ところで
今まで紹介したこれ
51
ReactiveProperty の方が
イケてますよね
52
http://reactiveproperty.codeplex.com/
スッキリ!
public class FifthViewModel {!
public ReactiveProperty<float> Red { get; private set; }!
public ReactiveProperty<float> Green { get; private set; }!
public ReactiveProperty<float> Blue { get; private set; }!
!
public ReactiveProperty<Color> Color { get; private set; }!
!
public FifthViewModel() {!
!
this.Color = Observable.CombineLatest(Red, Green, Blue, !
(x, y, z) => new Color(x, y, z)).ToReactiveProperty();!
!
}!
}
53
ReactiveUI で画面遷移
• MvvmCross では
• ShowViewModel<NextViewModel>()
• ReactiveUI だと
• Router.NavigateCommandFor<NextViewMo
del>() - 参照
• が、iOS/Android では未対応のようで…
54
画面遷移
• 画面遷移はプラットフォーム毎に仕組みが違って、
しかも進化が早い
• MvvmCross も、iOS の Storyboard に対応し
切れていない
• 割りきって View に画面遷移コードを書くか、

Resolver(後述)を使って自作する
55
IoC というか DI というか
• MvvmCross では
• Service を使う by @iseebiさん
• ルールに従えば Resolver を意識することない
• ReactiveUI では
• RxApp.MutableResolver を使う
• 全てコードを書く必要あり
56
Resolver の仕組み
r = RxUI.Resolver
HogeDroid
(Android)
HogeTouch
(iOS)
Model
IHogeService
r.Register(()=>new HogeDroid(), typeOf(IHogeService))
var svc = r.GetService<IHogeService>()
…PCL
57
public partial class AppDelegate : UIApplicationDelegate {!
public override bool FinishedLaunching(!
UIApplication app, NSDictionary options) {!
!
var r = RxApp.MutableResolver;!
!
r.Register(!
() => new HogeTouch(), !
typeof(IHogeService));!
または!
r.RegisterLazySingleton(!
() => new HogeTouch(), !
typeof(IHogeService));
Resolver へ登録
58
都度インスタンスを生成
初回1回だけ、あと使い回し
Resolver を使って
インスタンス化
// Model(PCL) にて!
!
IHogeService svc = !
RxApp.MutableResolver.GetService<IHogeService>();!
!
svc.SomeMethod(); // 実体は Resolver に登録したクラス
59
まとめ
60
ReactiveUI のメリット
• Rx で、(主に)ViewModel のコードを削減
• IoCコンテナ(Resolver)でPF固有機能を解決
• iOS/Android/WPF などの DataBinding が用意
されている
61
参考リンク
• neue cc 

http://neue.cc/
• かずきのBlog@hatena

http://okazuki.hatenablog.com/
• Rx入門 ¦ xin9le.net

http://xin9le.net/rx-intro
• GitHub's Xamarin starter apps

http://log.paulbetts.org/open-source-githubs-xamarin-
starter-apps/
62
ありがとうございました
63
おまけ
• Rx も PCL 化されましたが、
• Android用、iOS用のSystem.Reactive.PlatformServices.dll

は、どこに?

あるいはどうやってビルドしたらよいでしょう?
• あと PlatformServices.dll は

iOS の AOT で問題なく使えるのでしょうか?
• ref:https://twitter.com/atsushieno/status/
457399573363712000
64
おまけ
• PCL の Profile に Silverlight を含めると
IObservable が無くなってしまうので実質SLは
(ry
• Profile78 

This is Xamarin's current preferred profile.

とのこと。
65

Xamarin で ReactiveUI を使ってみた