二つのObservableでタイムアウトをチェック
Rxのお勉強。
Rxのタイムアウトって一つのシーケンスの間隔でタイムアウトを管理するのですが、やりたい事は二つのObservableを組み合わせるパターン。
一つ目のObservableがOnNextされてから二つ目のObservableがOnNextされるまでの間にタイムアウトが発生したらErrorを流す。
リクエストとレスポンスを同期させてタイムアウトも管理したいイメージです。
public static IObservable<TResult> SyncTimeout<TSource, TResult>(this IObservable<TSource> first, IObservable<TSource> second, TimeSpan dueTime, Func<TSource, TSource, TimeSpan, TResult> selectorFunc) { var firstStamp = first.Select(x => Tuple.Create(x, DateTime.Now.Ticks)); var secondStamp = second.Select(x => Tuple.Create(x, DateTime.Now.Ticks)) .Merge(firstStamp.Delay(dueTime).Select(x => Tuple.Create(x.Item1, (long) 0))); return firstStamp .Zip(secondStamp, (f, s) => new {First = f.Item1, Second = s.Item1, Span = s.Item2 - f.Item2}) .Take(1) .Repeat() .Select(x => { if (x.Span < 0) throw new TimeoutException(); return selectorFunc(x.First, x.Second, TimeSpan.FromMilliseconds(x.Span)); }); }
なんかもっとうまく書けないですかねぇ(´・ω・`)
アドバイスいただけたりするとうれしいです。
その後
アドバイスいただいてこんな感じにできました。
MVVMっぽくXamarin.Formsアプリ作ってみました。その4
続きです。
MVVMっぽくXamarin.Formsアプリ作ってみました。その1 - 眠いしお腹すいたし(´・ω・`)
MVVMっぽくXamarin.Formsアプリ作ってみました。その2 - 眠いしお腹すいたし(´・ω・`)
MVVMっぽくXamarin.Formsアプリ作ってみました。その3 - 眠いしお腹すいたし(´・ω・`)
コントローラインターフェースの実装
この画像の構成はPCLブロジェクトにて行われていますが、コントローラインターフェースの実装はPCLでは行っていません。
GitHub - yuka1984/XamarinFormsSampleStopWatch: Xamarin.Formsサンプルアプリ
今回のサンプルアプリではコントローラインターフェースはPCLプロジェクトのSwLib.Interfaceで作成していますが、インターフェースに対する実装はSwLib.Core、SwLib.Core.Droid、SwLib.Core.Touchで行っています。
SwLib.Coreは普通のクラスライブラリプロジェクト
SwLib.Core.DroidはAndroid用クラスライブラリプロジェクト
Swlib.Core.TouchはiOS用クラスライブラリプロジェクトになります。
これから説明する方法はUWPプロジェクトへの展開を意識していません。
あくまでもAndroidとiOSをターゲットとしたクラスライブラリの作成方法となります。
私は以下の手順でコントローラの実装を行っています。
- クラスライブラリプロジェクト(SwLib.Core)を作成してインターフェースを宣言しているPCL(SwLib.Interface)を参照設定する。
- インターフェースに対する実装クラスを作成する。
- 作成したクラスの単体テストSwLib.CoreTestsを作成して試験を行う。
- Androidクラスライブラリ(SwLib.Core.Droid)を作成してPCLを参照設定する。
- SwLib.Coreの.csファイルをリンクとしてSwLib.Core.Droidに追加する。
- iOSクラスライブラリ(SwLib.Core.Touch)を作成してPCLを参照設定する。
- SwLib.Coreの.csファイルをリンクとしてSwLib.Core.Touchに追加する。
SwLib.Coreの実装では普通のライブラリプロジェクトとAndroid、iOSのプロジェクトですべてファイルが一緒ですが、例えばコントローラインターフェースが複数あって一つのインターフェースに対してWindowsとiOSは共通だけどAndroidに関してはJavaを使って実装するなどという場合には、そのクラスの実装はリンク追加せずAndroidプロジェクトで実装を行います。
このような実装手順とプロジェクト構成はコントローラインターフェースだけでなく、共通化クラスにも適応します。
例としてKeyValueStoreというライブラリ実装を用意しました。
KeyValueStoreの実装
KeyValueStoreと言っていますが、簡易のデータ保存ストアです。
プロジェクト構成は
KeyValueStore.PCLはIKeyValueStoreというインターフェースを持っています。
KeyValueStore.CoreはIKeyValueStoreの.NET46用実装です。
KeyValueStore.Core.DroidはIKeyValueStoreのAndroid実装です。
KeyValueStore.Core.TouchはIKeyValueStoreのiOS実装です。
このライブラリはNuGet Gallery | SQLite.Net PCL 3.1.1を使用してIKeyValueStoreに対する実装を行っています。
Windowsクラスライブラリのファイルは3つ
XamarinFormsSampleStopWatch/Kvs.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub
IKeyValueStoreに対するabstractの実装です。
各プラットフォームで差分がない部分を実装して差分をabstractとしています。
KeyValueStoreBaseのWindows用実装です。
Kvs.csとKeyValueStoreBaseはAndroid/iOSプロジェクトへリンク追加しますが、KeyValueStore.csはリンク追加せず、それぞれのプロジェクトで作成しています。
このプロジェクトではあまり実装に差分はありません。
DependencyInjectionを使用すれば吸収できる範囲だとは思います。
KeyValueStoreというプロジェクトを単独で切り離せる用にするために、このような形となっています。
今回のサンプルではXamarin.FormsプロジェクトとSwLib、KeyValueStoreを一つのソリューションで開発していますが、それぞれを別のソリューションで作成する事も可能な依存関係となっています。
ソリューションを分けた上でローカルnugetを使用した結合を行いXamarin.Formsアプリのソリューション自体を小さくすることができたりしますしロース管理のリポジトリを分けて開発することもできます。
今回は以上です。
次回、再度Xamarin.Formsプロジェクトに戻ってDIContainerに関して説明を行って終了にしたいと思います。
毎度細切れで申し訳ありません('ω')
Xamarin iOSのバインディングライブラリを組み込んだらエミュレータで動かせなかった
Xamarin iOSでネイティブのライブラリを使うためにバインディングライブラリを作ってプロジェクトに組み込んだりします。
作り方とかはこの辺を見たりします。
で組み込んでエミュレータで動かそうとビルドするわけですよ。エラーが発生するわけですよ。
アーキテクチャが対応してないよって。
.aファイルのサポートアーキテクチャがARMv7+ARM64だったりするわけで当然ですよね。
バインディングライブラリは参照に追加しているのでこのままではエミュレータで動かせなくなってしまう(´・ω・`)
Bindingライブラリを使っている機能は動かなくてよいからエミュレータで動作させたかったので以下のことをしました。
ちなみにバインディングライブラリは大体こんな感じで作ってます。
public abstract class HogeBase : IHoge { public static class NativeMethods { [DllImport("__Internal")] public static extern void Init(); [DllImport("__Internal")] public static extern void Start(); [DllImport("__Internal")] public static extern void Stop(); } public abstract void Start(); public abstract void Stop(); }
IHogeはAndroidとごにょごにょ・・・・
でXamarin iOSプロジェクトでは
public class Hoge : HogeBase { private static Lazy<Hoge> _instance = new Lazy<Hoge>(() => new Hoge()); public static Hoge GetInstance() => _instance.Value; private Hoge() { NativeMethods.Init(); } public override void Start() { Debug.WriteLine("start"); NativeMethods.Start(); } public override void Stop() { Debug.WriteLine("stop"); NativeMethods.Stop(); } }
こんな感じで実装していました。
構成がDebugでプラットフォームがIPhoneSimulatorの時に
__SIM__
というコンパイルシンボルを追加。こんな風に書き換えちゃう
#if __SIM__ public class Hoge : IHoge { private static Lazy<Hoge> _instance = new Lazy<Hoge>(() => new Hoge()); public static Hoge GetInstance() => _instance.Value; private Hoge() { } public void Start() { Debug.WriteLine("start"); } public void Stop() { Debug.WriteLine("stop"); } } #else public class Hoge : HogeBase { private static Lazy<Hoge> _instance = new Lazy<Hoge>(() => new Hoge()); public static Hoge GetInstance() => _instance.Value; private Hoge() { NativeMethods.Init(); } public override void Start() { Debug.WriteLine("start"); NativeMethods.Start(); } public override void Stop() { Debug.WriteLine("stop"); NativeMethods.Stop(); } } #endif
要するにIPhoneSimulatorで動かすときはバインディングライブラリを使わないっていうだけです。 参照でバインディングライブラリを追加していたとしても実際に使われなければビルドできます。
この問題ってどうやって解決すればよいか教えてください(´・ω・`)
MVVMっぽくXamarin.Formsアプリ作ってみました。その3
前回からの続きです。
MVVMっぽくXamarin.Formsアプリ作ってみました。その1 - 眠いしお腹すいたし(´・ω・`)
MVVMっぽくXamarin.Formsアプリ作ってみました。その2 - 眠いしお腹すいたし(´・ω・`)
ほんとに細切れでごめんなさい。
View
XAMLです。Bindingします。
ReactivePropertyをBindingすると時はProperty名.Valueとなるので注意です。
今回のサンプルでは作成していませんがUserControlを作ったりCutomRenderを作成したりする事は結構あります。
Xamarin.Forms2.1からはEffectsが追加されていますが試してないです。
WPFなどではIllustratorとかPhotoShopなどからXAMLを生成してコントロールを作ったりするのですが、Xamarin.Formsの見た目は絵を多めに使ったりしました。
View回りに関してはデザインとどのように連携するかなどのノウハウがもっとほしいなぁって思ってます。
Viewの機構的には色々と仕組みはありますが
あとはBehavior、WPFとかやってきてる人にはおなじみですよね。たぶん。
例えばボタンがクリックされた時にボタンのCommandプロパティにICommandをBindingしているとICommandがExecuteされます。
Xamarin.Formsでは無いですが、ボタンがマウスオーバーされたときにコマンドを実行したいような時にButtonを継承して新しいコントロールを作成するのでなくButtonにアタッチするような形でコマンドをバインドできる仕組みを作ることができたりもします。
またサンプルにはありませんがローカライズも正解がなくて悩みます。
前回も触れましたがページ遷移は悩みます。
実際の案件ではPageManagerクラスを作成してMessagingCenterで繋いで管理する形をとっていました。
PageManagerではコンテナーを使ってController、View、ViewModelのインスタンス管理とページ遷移を管理していました。
Navigation
Xamarin.Formsの基本的なページ遷移機構になります。
INavigation Interface - Xamarin
こんな感じのインターフェースを使ってページ遷移を行います。
NavigationはVisualElementクラスのプロパティです。
VisualElementを継承しているPageクラスはもちろんButtonにもNavigationはついています。
ある意味で広範囲からページ遷移を行うことができたりします。
ただNavigationをプログラムから呼び出さなくてもページ遷移を行うことができたりします。
それにはMultiPageを継承したPageを使います。
クラス関係はこちらのURLにある画像がわかりやすいです。
Introducing Calcium for Xamarin.Forms - CodeProject
MultiPageはabstractであり実装はCarouselPageとTabbedPageが用意されています。
この二つはIList
要するにChildrenにページをセットしておけば、それらのページ間の移動をUI操作に応じて勝手にやってくれるんです。
TabbedPageはタブでCarouselPage*1はスワイプでページ遷移を行うことができます。
イメージとしてはINavigationを使用したページ遷移はスタック・縦方向のページ遷移でMultiPageを使用したページ遷移は横方向のページ遷移になります。
XamarinFormsSampleStopWatch/App.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub
今回のサンプルではStopwatchPageとSaveListPageはTabbedPageで横方向のページ遷移とし、SavePageへの移動はNavigationを使用して縦方向のページ遷移を行っています。
注意する点としてMultiPageのページ遷移は早いけどNavigationのページ遷移は早くないということがあります。
今回程度の画面であればほとんど気にならないレベルなのですがViewが複雑化したりBindingが増えたりするとNavigationにプッシュしたりした時の動作が重くなりユーザ操作がスムーズでなくなります。
この事はアプリを作るうえでユーザ体験をよくするためにも凄く大切なのでその辺を踏まえて設計を行ったほうが良いと思います。
今回はここまで。あんまりプログラムの解説してないですね(´・ω・`)
*1:CarouselPageは非推奨になるらしいです。
MVVMっぽくXamarin.Formsアプリ作ってみました。その2
前回
MVVMっぽくXamarin.Formsアプリ作ってみました。その1 - 眠いしお腹すいたし(´・ω・`)
の続きから
ViewModel
ViewModelですがINotifyPropertyChangedは実装しません。
ReactivePropertyを使用します。
コントローラの保持するモデルからReactiveProperty、ReactiveCollection、ObservableCollectionなどを作成します。
表示用のプロパティは基本的にReadOnlyReactivePropertyとしています。
入力用プロパティはReactivePropertyを使用します。
配列は基本的にReadOnlyReactivePropertyとしますが、必要であればObservableCollectionを作成します。
コマンドはReactiveCommandを使用します。
ReactiveCommandは他のReactivePropertyを元に実行可能状態を管理します。
ClearCommand = IsRuning.CombineLatest(elapsedtime, (run, t) => t > 0 && !run).ToReactiveCommand(); ClearCommand.Subscribe(x => { IsRuning.Value = false; IsStart.Value = false; stopWatchController.Clear(); });
コンストラクタでコントローラを受け取りReactivePropertyを作成していきます。
今回のサンプルではModelのプロパティは経過時間のmsecのlong値でしかないですが、それをRxで変形させてプロパティにしています。単純なlong値が時間フォーマット文字列に変形したり時計の針の角度に変形したりします。
Rotation = stopWatchController.StopWatchModel.ObserveProperty(x => x.ElapsedTime).Select(x => (double) (x/1000*6%360) - 90).ToReadOnlyReactiveProperty(); Time = stopWatchController.StopWatchModel.ObserveProperty(x => x.ElapsedTime).Select(x =>x.ToTimeFormat() + "->").ToReadOnlyReactiveProperty();
ロジックの結果であるModelは実体としてのデータであって表示のことは意識していないためコントローラの実装者はロジックの事に専念して実装を行うことができてUI担当者は実値を変形させることでUIを作成することができます。
ReactivePropertyを使用するとViewModelとロジック部をスッキリと分離することができとても良いです。当然Rxな書き方が可能となるためプロパティ同士の合成ができたり色々とつよいです。
ViewModelの再利用は考えていません。 ViewModelの再利用に関しては、過去いろいろ試したりしたけどうまくいったことがないので私の設計能力では無理だと思ってそれ以降考えたことはないです。
ViewModelの単体試験ですが、こちらも考えていません。 ただできないことはないかなぁと思ってます。 単体試験用のコントローラを注入してReactivePropertyのSubscriveにて値を記録するような形式でのテストが可能であると考えます。
ViewModelからのページ遷移
今回のサンプルではMessagingCenterを使用しています。
Publish and Subscribe with MessagingCenter - Xamarin
Appクラスのコンストラクタでページ遷移をSubscribeで実装しています。
XamarinFormsSampleStopWatch/App.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub
ViewModelからSendしてページ遷移を呼び出します。
ToSaveCommand.Subscribe( x => MessagingCenter.Send(new NavigationMessage {IsModal = true}, NavigationMessage.ToSavePage));
正直、この実装はどうかな?って感じです。
もっと良い方法があれば知りたい感じです。
ページ間でのデータの受け渡しは必要に応じます。
今回のサンプルではロジック部の状態保持のみで実装可能であったためViewModel間での値の受け渡しは行っていませんが、例えばリストから詳細ページに移動するようなケースでは引数の受け渡しを行ったりします。
注意する点は画面遷移の為のデータ保持をコントローラで行ってはいけないということです。
今日はここまで。 ちょっとずつでごめんなさい。
MVVMっぽくXamarin.Formsアプリ作ってみました。その1
はじめに
世の中、すでにXamarin.Formsを使って作られたサンプルアプリはたくさんありますし、公式でもサンプルはあります。
私が今回作ってみたのはオレオレデザインパターンを適用して作ったアプリです。
Xamarin.Formsで一般的に使用されるデザインパターンはMVVMと呼ばれるものが多いのだと思います。
正直に申しまして、私自体MVVMって結局何なのだろう?っていう状況です。 私にとってはすごく難しいんです。 いろいろなサンプルを見るのですが各々結構違っていたりするんです。 マサカリが飛びまくっているんです。
Xamarin.Formsアプリを開発するにあたってどのようなデザインにするかすごく悩みました。
結局、MVVMはよくわからないから今ある知識でどうやったら今回の開発チームで効率よく開発できるか。
ということを考えてデザインパターンを設計することにしました。
- 少人数だけど分業して開発を行う必要がある。
- 機能ごと縦割ではなく、UIとロジックの横割で開発したい。
- UIとロジックは並行して開発したい。
- 最悪Xamarin.Forms無理ってなった時にもロジックは生かしたい。
この4つを意識して設計を行いました。
今後の勉強のためにも意見を頂けると助かります(ただし心が折れない程度に限る)
サンプルアプリ
今回作成したサンプルアプリケーションは
に公開をいたしました。
お題はJXUGであったストップウォッチアプリです。
ラップ機能付きストップウォッチ、結果保存、結果閲覧機能を実装しました。
kazukiさんの実装から劣化している感じになります"(-""-)"
すいません。iOSでテストしてません。WinPhoneは動きません。
デザイン
ばん(^^)/
こんな感じです。 すいません。こういう図を描くのが下手ですし説明なども下手です。 このBlogは私の復習とか説明練習も兼ねています。ほんとごめんなさい。
この図でいうControllerInterfaceの部分がロジック部になります。
そしてViewModelとView、MessagingCenterとNavigationがUI部になります。
概念の図なのですがライブラリ名とかクラス名が入ってしまっていますが今回の設計はそれらに依存している部分があるためです。
ControllerInterface
ロジッククラスのインターフェースです。
メソッドとModelInterfaceクラスをプロパティとして公開しています。
メソッドの呼び出しに対して結果をModelクラスに反映します。
またタイマーなどでの定周期処理に結果をModelクラスに反映したりします。
なぜインターフェースかというとViewModelを作る段階で実装ができていないからです。
開発の手順としては
最初にコントローラインターフェースおよびモデルインターフェースの設計を行い作成をします。
ロジック担当者はインターフェースに対する実装を別プロジェクトにて作成します。(サンプルではSwLib.Coreというプロジェクトにて実装してます。)
UI担当者はインターフェースに対するモッククラスを作成しながらViewModelを作成していきます。
ModelInterface
モデルクラスはINotifyPropertyChangedを実装しているのでプロパティの変更を監視できます。
プロパティはReadOnlyのみで定義し外部からの変更を受け付けません。
配列型の場合にはReadOnlyObservableCollectionにて実装されているので変更を監視できます。
配列型も外部からの変更を受け付けません。
なぜモデルまでインターフェースになっているかなのですがINotifyPropertyChangedの実装をどうするか決めていなかったからです。
なぜ公開しているプロパティがすべてReadOnlyかというとUI部側から勝手に変更されるように事を避けたかったからです。開発初期段階でチーム参加者がデザインパターンに不慣れだったこともあって意図しない実装を設計的に防げればなぁという感じでした。
今回はここまでで、また続き書きます。
C#でRakuten MA使って形態素解析してみた。
夜遅くにツイッターみてたらフォローさせていただいてる絵空事さんがこんなのツイートしてました。
Rakuten MAってなに?
ってことでちょっと調べてみました。
JavaScriptの形態素解析をするライブラリみたいです。
この手の分野は全然疎くて.NETで形態素解析っていうとNMeCabなぁってことくらいしか知りませんでした。
このライブラリはブラウザ上かnode.jsがあれば実行できるって言う事が分かりましたが
そういえばJavaScriptをC#上から実行ってできないのかな?
ってふと思いました。
javascript C# 実行 なんてキーワードで検索したらASP.NETに関する結果ばかりヒットしてしまいました。
検索を進めると
こんな記事を発見(*'▽')
node.jsとEdge.jsの間の接続はシームレスで双方向である
(; ・`д・´)
C#からnode.jsの環境で実行可能ならRakutenMA使えるんじゃない?
ってことでEdge.jsについて調べてみました。
日本語でヒットしたのはこれくらい
これ、確実にRakutenMA実行できるじゃん(*'▽')
っていうことでとりあえず実行できるようにしてみました。
めんどくさいのでスクショばん(≧◇≦)
やったことはとりあえず
- コンソールプロジェクト作る
- Edge.jsとNewtonwoft.Jsonをnugetより追加
- RakutenMAをgithubより取得してプロジェクトに追加
- ファイルを全部ビルドアクション:なし 出力:常にコピーするに変更
- プログラム書く
- つまづく
- 結果これ
すばらしい(*'▽')
プログラムは
ここを参考にしました。
jsコードはdemo.jsから抜粋
つまづいた点はrequireのパスとfsのファイルパスでrequireのパスはとfsのCurrentが違っていてアレ?ってつまづきました。
C#からJavaScriptが実行できて結果を受け取れちゃう世の中です。
凄い世の中です。
RakutenMAだけじゃなくて色んな事に応用できそうですねぇ。
以上