Xamarin で ReactiveUI を使ってみた

3,274 views

Published on

2014/4/19 スマートフォン勉強会(すまべん) 「Xamarin 2.0であそぼう!」@関東 の発表資料です。

Published in: Technology
0 Comments
15 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,274
On SlideShare
0
From Embeds
0
Number of Embeds
133
Actions
Shares
0
Downloads
14
Comments
0
Likes
15
Embeds 0
No embeds

No notes for slide

Xamarin で ReactiveUI を使ってみた

  1. 1. Xamarin で ReactiveUI を使ってみた 2014.4.19 すまべん特別編「Xamarin 2.0であそぼう!」@関東 1
  2. 2. 自己紹介 • @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
  3. 3. •        (ロジネビュラ) • 千年先まで費用ゼロのクラウド型倉庫管理システム •       (ジクウ) • リアルタイム位置データ収集プラットフォーム • Nepula(ネプラ) • 基幹業務システム向けPaaS おもにココ担当 クラウドサービスプロバイダ 3
  4. 4. HexRinger developed at ハマッカソン #2 ref http://blog.airs.co.jp/2010/12/06/hamackathon-20101204.html 4
  5. 5. しゃべPOI developed for OpenStreetMappers 続きは… 位置情報系Androidアプリケーションの開発 - Togetterまとめ 5
  6. 6. 富士フォト with ふじのくにオープンデータ MashupAward9, アーバンデータチャレンジ東京2013 ref シビックハックの広まりと地方エンジニアの躍進 ‒ MA9総括 ¦ finder 6
  7. 7. 富士フォト with ふじのくにオープンデータ MashupAward9, アーバンデータチャレンジ東京2013 7 iOS版は Xamarin で 作りました
  8. 8. おことわり • 資料内の C# コードはスペース節約のため「後 ろ { カッコ」にしています • Xamarin の勉強会なので iOS/Android 中心の 話をします • Mac が主PC なので Xamarin Studio を使って います 8
  9. 9. 目次 • MvvmCross ではダメ? • Reactive Extensions について • ReactiveUI とは • まとめ 9
  10. 10. MvvmCross ViewModel Model View(Droid) View(iOS) IHogeService HogeDroid HogeTouch PCL PF毎のView+DataBinding PF固有機能(カメラ、アプリ連 携、GPS…) 10
  11. 11. MvvmCross • Xamarin でクロスプラットフォームアプリを開 発する際の事実上標準のMVVMフレームワーク • Android, iOS, WinStore, WP, Mac などに対応 • M-VM を PCL で共通にできる • プラットフォーム依存機能は IoCコンテナで 11
  12. 12. これで良くね? 12
  13. 13. MvvmCross のデメリット • 全方位&高機能が故にFat! • アセンブリ数:15(プラグインも入れると 40over) • チュートリアル動画が40本以上もある • ルールたくさん(Binding、ValueConverter、 Service、Plugin…) 13
  14. 14. MvvmCross のデメリット • クロスプラットフォームな為、機能が最大公約数 • 前の画面からの戻り値を受け取れない(Android の onActivityResult)とか • 各PFの最新機能に追いつけない • iOS - Storyboard • Android - Fragment 14
  15. 15. MvvmCross 以外の 選択肢 15
  16. 16. QuickCross • https://github.com/MacawNL/QuickCross • No-Binary! • PowerShell でコマンド叩くとコードを生成 • Mac なので試せませんでした… 16
  17. 17. propertycross.com 各SDKでクロスPF開発するための指針が書いてあるみたい 圧倒的じゃないか… 17
  18. 18. そして ReactiveUI 18
  19. 19. …の前に ! Reactive Extensions ! について 19
  20. 20. Reactive Extensionsとは • https://rx.codeplex.com/ • 非同期処理やイベントを LINQ っぽく書いてチェ インできるスゴイやつ • TPL(async/await含む) と比べると、複数の結果 を返せるのでストリーム処理に便利 20
  21. 21. 私とRx • Android の非同期処理でコールバック地獄 • jQuery.Deferred みたいなの欲しい • 「.NET には Rx がある」というのを知る
 ref:Reactive Extensionsで非同期処理を簡単に by @neueccさん
 「なにこれスゴイ!」 • Java には Rx ないの? 21
  22. 22. Java でも Rx • reactive4java - 開発終了 • Androidアプリ開発で使ってます • LINQ to Objects もあるよ • RxJava • 事実上標準の Rx for Java になる気配 • Netflix なので安心なんじゃないでしょうか 22
  23. 23. こうなる事は分かってた // 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);
  24. 24. で ReactiveUI とは 24
  25. 25. ReactiveUI • http://www.reactiveui.net/ • Reactive Extensions を全面的に採用した MVVMフレームワーク • WPF, WP, WinStore, iOS, Android, Mac 対応 • 元Microsoft で GitHub の中の人が作ってる 25
  26. 26. MvvmCross のこれ… ViewModel Model View(Droid) View(iOS) IHogeService HogeDroid HogeTouch PCL 26
  27. 27. ReactiveUI でもできます ViewModel Model View(Droid) View(iOS) IHogeService HogeDroid HogeTouch PCL IObservable IObservable IObservable M-V-VM の「つなぎ」に Rx を使ったら… …もっと COOL に! 27
  28. 28. 基本的なクラス構成 MvxViewModel : INotifyPropertyChanged MvxActivity MvxViewController … MvvmCross ReactiveObject : INotifyPropertyChanged ReactiveActivity ReactiveViewController … ReativeUI View ViewModel 28
  29. 29. 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
  30. 30. 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
  31. 31. 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();! }! }
  32. 32. 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
  33. 33. 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
  34. 34. 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
  35. 35. 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
  36. 36. 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
  37. 37. 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. 38. 実行したところ 38 vm.Register.Subscribe( _=> /* 実行した時の処理 */)
  39. 39. Command実行∼ Property更新を 流れるように 39
  40. 40. シナリオ 40 Model ! .GpsAvailable .GetLocations() ViewModel ! .Location .StartGps() View bind notify listen notify
  41. 41. 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
  42. 42. 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
  43. 43. 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
  44. 44. 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
  45. 45. 複合条件 (Rx を活かして) 45
  46. 46. シナリオ 46 ViewModel ! .Red .Green .Blue .Color View bind notify depends
  47. 47. 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
  48. 48. 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つの値が ったら初回設定、 以降はいずれかが変わったら設定
  49. 49. 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に変換
  50. 50. 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. 51. ところで 今まで紹介したこれ 51
  52. 52. ReactiveProperty の方が イケてますよね 52 http://reactiveproperty.codeplex.com/
  53. 53. スッキリ! 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
  54. 54. ReactiveUI で画面遷移 • MvvmCross では • ShowViewModel<NextViewModel>() • ReactiveUI だと • Router.NavigateCommandFor<NextViewMo del>() - 参照 • が、iOS/Android では未対応のようで… 54
  55. 55. 画面遷移 • 画面遷移はプラットフォーム毎に仕組みが違って、 しかも進化が早い • MvvmCross も、iOS の Storyboard に対応し 切れていない • 割りきって View に画面遷移コードを書くか、
 Resolver(後述)を使って自作する 55
  56. 56. IoC というか DI というか • MvvmCross では • Service を使う by @iseebiさん • ルールに従えば Resolver を意識することない • ReactiveUI では • RxApp.MutableResolver を使う • 全てコードを書く必要あり 56
  57. 57. Resolver の仕組み r = RxUI.Resolver HogeDroid (Android) HogeTouch (iOS) Model IHogeService r.Register(()=>new HogeDroid(), typeOf(IHogeService)) var svc = r.GetService<IHogeService>() …PCL 57
  58. 58. 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回だけ、あと使い回し
  59. 59. Resolver を使って インスタンス化 // Model(PCL) にて! ! IHogeService svc = ! RxApp.MutableResolver.GetService<IHogeService>();! ! svc.SomeMethod(); // 実体は Resolver に登録したクラス 59
  60. 60. まとめ 60
  61. 61. ReactiveUI のメリット • Rx で、(主に)ViewModel のコードを削減 • IoCコンテナ(Resolver)でPF固有機能を解決 • iOS/Android/WPF などの DataBinding が用意 されている 61
  62. 62. 参考リンク • 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. 63. ありがとうございました 63
  64. 64. おまけ • Rx も PCL 化されましたが、 • Android用、iOS用のSystem.Reactive.PlatformServices.dll
 は、どこに?
 あるいはどうやってビルドしたらよいでしょう? • あと PlatformServices.dll は
 iOS の AOT で問題なく使えるのでしょうか? • ref:https://twitter.com/atsushieno/status/ 457399573363712000 64
  65. 65. おまけ • PCL の Profile に Silverlight を含めると IObservable が無くなってしまうので実質SLは (ry • Profile78 
 This is Xamarin's current preferred profile.
 とのこと。 65

×