前置き
去年の夏くらいから開発をしていたXamarin.Forms採用のアプリのベータリリースが近づいてきたので、この辺りでXamarin.Formsでの開発で思った事とか苦労している所とかまとめてみようかと思います。
はてなブログ初エントリーです。
あんまり物書きする習慣がないのですけど、最近はQiitaへの投稿も増えたしソースコードとかを書かない内容の物はブログに書いていこうかと思います。
なんでXamarin.Formsを採用したの?
私がC#好きだからです。嘘です・・・・よ・・・
今回作成しているアプリはWindows向けアプリのスマホ版なのです。
そしてWindows向けアプリはC# + WPFで作成されています。6割くらいの機能は共通です。
機能の8割はMonacaなどのハイブリッド開発が可能なのですが、メインというか売りの機能がそれでは無理でした。
その売りの機能は、TCP/UDP通信、高速なバイナリ操作、ストリーミング音声再生などをする必要があります。
ということでQtとかも検討していましたが、元がC#ならXamarinを使わない手はないということでXamarin採用となりました。
Unityも検討していましたが、学習コストが高くて見送りました。
処理パフォーマンスも画面描画パフォーマンスもまともに書けば問題ないことは、調査段階で確認をしました。
ただ、正直Formsの採用は最後まで迷っていました。
これは顧客からの(´・ω・`)な要望に対して泥沼化するのではないか、ということが想像できたからです。
最終的には開発効率と最悪プラットフォーム毎にコードを書けば何とかなると考えてXamarin.Formsを採用しました。
どんな風に開発しているの?
クライアントの開発メンバーは多い時で3人、今は2人とかです。私ともう一人みたいな感じです。
主にUI回りと通信回りで分業してます。私は通信回りとバックエンドも参加しているのでXAMLを触ることは少ないです。
通信系のライブラリを作成する。と View ViewModelを作成する、を同時に進めました。
先に通信ライブラリのinterfaceを一つのライブラリとして作成し、View、ViewModelの作成は通信ライブラリのモックを作ってもらいながら進める形をとりました。 ライブラリの管理はUnityContainerを使っています。
通信ライブラリはinterfaceに対する実装を行うだけです。
通信ライブラリとの結合が進んでからはViewの大幅な見直しが必要な時以外はモックライブラリは不要となりました。
どんなライブラリを使っているの?
Prism.Mvvm
BindableBase と DelegateCommandを使ってます。
Unity
DIContainerとして使ってます。ライフサイクルとかカスタマイズできて良いです、
ReactiveProperty(Rx)
ViewModelとPCLでのタイマー。
通信ライブラリの中にはINotifyPropertyChangedを実装したModelがあって、そのモデルをReactivePropertyで監視しているデザインにしています。
通信ライブラリは状態変化をModelのプロパティを書き換えるだけです。
Modelの複数のプロパティをReactiveProperty化して、それらをマージしてセレクトして別のReactiveProperty作るとかすごく便利です。
SQLite.NET
データ保存用に。SQLite.NETをラップしてKeyValueライブラリを作成して使ってます。
Newtonsoft.Json
必要ですよね。
Xamarin.Insights
運用になったら必須だと思うし、試験段階でもかなり便利です。
「あれ、落ちちゃった( ;∀;)」みたいなときに数分後にはXamarin.Insightsで確認できます。
Xamarin.Formsって楽なの?
楽ではないと思います。見た目の部分は大変です。
iOSとAndroidで見た目を共通XAMLで定義出来てバインディングできるというのは素晴らしい事ですが、iOSとAndroidでフォントサイズも違えば画面サイズも違います.
デザイナーがないので両方動かして確認しながら調整して妥協点を探していくのですが、めんどくさいです。
実際にその調整作業を考えたら、見た目は別々に定義してもいいんじゃないかと思うくらいです。
見た目に関して緩く作っても問題ないプロジェクトにはお勧めです。
ロジックに関してはかなり楽できます。プラットフォーム毎に書かなくて良いのは良い事です。
現在のところ、ロジック部分で端末固有の制限とかバグとかは出ていません。
結局、プラットフォーム毎にコードを書いた部分は
- UI部品
- デバイス情報取得
- 音声再生
- プッシュ通知実装
くらいです。
Xamarin.Formsで大変な部分は?
少し書きましたが、iOSとAndroidでUIを調整するのは面倒です。
早く専用エディターができると良いなぁと思います。
あとはタブレットサイズとスマホサイズを同一のデザインで表現しようとすると大変です。あまりXamarin.Forms関係ありませんが。
できればタブレットとスマホサイズは別デザインにするべきです。大変です。ほんとに。
文字の大きさなどを調整するためにコントロールを用意したりして対応しました。
Navigationに特殊性を持たせるとかなり大変だと思います。
Xamarin.Formsには定番のナビゲーションがそれなりに用意されていますが、UIデザインは限定されます。
例えばタブデザインでボタンデザインも上か下かも含めて見た目を同じにしたいみたいなことを言われると標準のNavigationでは対応できないのでゴリゴリ書く事になります。
逆に標準のNavigationで対応できる範囲であれば楽です。
あとは、NavigationがiOSとAndroidで挙動が異なる時があったりします。非同期を絡めた場合などに出てきたりします。
その時はロジックを調整して実行順序を変えたりしてうまくいくように調整したりします。
現時点ではこの部分以外で、とても大変となった点は特にありません。ですが、ちょっとした工夫で解決できる範囲の事はそれなりにあります。
Xamarin.Formsで工夫した点は?
自分なりに考えて色々と工夫した点はあります。
大きいところとしてはプロジェクト構成です。
なんでもPCLでやろうとすると詰まります。でもSharedはちょっと・・・みたいに感じる部分がありました。
あと単体テストを効率よくやりたい面もありました。
そこで、ビジネスロジック、コアロジックは別プロジェクトで開発し、ローカルnuget経由でXamarinプロジェクトに取り込むようにしました。
コアロジックは別に開発してWindows環境でテストを十分にテストを行ってから反映するようにしています。
ビジネスロジックのプロジェクトは
- PCLプロジェクトでインターフェースを定義する。これは一つのライブラリとしてローカルnugetにて展開する。
- 標準のクラスライブラリプロジェクトにインターフェースライブラリを取り込んでインターフェースに対する実装を行う。
- Android・iOSのクラスライブラリプロジェクトを作成してインターフェースプロジェクトを取り込み、標準プロジェクトの実装ファイルをリンク参照でインターフェースの実装を取り込む。
- ビルドして問題ないことを確認する。
- 標準クラスに対して単体テストプロジェクトを作成して単体テストする。
- ローカルnugetにて展開する。
みたいな形で運用しました。
また、極力DependencyServiceを使うことを避け、機能単体で流用性を持つものは別プロジェクトでライブラリ化するようにしました。
例えばKeyChainなどを使ってユーザ・パスワードを保存する仕組み、SQLiteを使ったKeyValueStoreなど設計段階でライブラリ化可能で他に使える機能部分はローカルnuget経由で他に展開できるようにしました。
当たり前といえば当たり前なのですが、Xamarin.Formsみたいな開発では特に効力が高くなるかと思います。
私自身、勉強会等に参加していないですし他の方がどういうやり方しているのか全然わからない中で自分なりに考えた方法ですが、
もっとうまくやる方法はあるかと思いますので調査してみてください。
最後に
私自身はハイブリッドアプリで開発可能なものはハイブリッドアプリで開発する方が良いと思っています。
ただ、一部にハイブリッドアプリでは実現が難しい機能などが存在する場合にはXamarin.Formsは一つの答えだと思います。
既にC#で書かれたPC版ソフトが存在し、コード共有できそうな場合にもXamarinは選択肢としてあり得ると思います。
ただ、それでもコード共有は大変だと思います。色々と問題に突き当たります。
それでも