xamarin で reactiveui を使ってみた
DESCRIPTION
2014/4/19 スマートフォン勉強会(すまべん) 「Xamarin 2.0であそぼう!」@関東 の発表資料です。TRANSCRIPT
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 forOpenStreetMappers
続きは… 位置情報系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 - Fragment14
MvvmCross 以外の 選択肢
15
QuickCross
• https://github.com/MacawNL/QuickCross
• No-Binary!
• PowerShell でコマンド叩くとコードを生成
• Mac なので試せませんでした…
16
そして 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
PCLIObservable
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 っぽい ViewModelpublic 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 な Commandpublic 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
ViewModelpublic 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 は普通に Bindingpublic 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
notifydepends
ViewModelpublic 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<NextViewModel>() - 参照
• が、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>()
…PCL57
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@hatenahttp://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