いろんなツールがあって技術選択に迷うので、定点観測的にいま思っていることをざっくり書いてみました。
TL;DR
- ひたすらスピードを求められるプロトタイピングなどはCordova(Ionicなど含む)
- Cordova上でReactで開発した資産を引き継ぎたいみたいな前提があってユーザーレスポンスを改善するならReact Native
- 時間がかかってもいいからとにかく安定させたいならネイティブ
- 学習コストが高くてもよくて開発スピードと安定性を両立するならXamarin
私はネイティブが一番好きです。
はじめに
「AndroidとiOSアプリを両方作ってくれ」という要件はよくある話ですが、ベンチャーで新しいサービスをモリモリと立ち上げている中での両対応となると、両方共ネイティブで作っていくわけにはいかないこともあります。
- そもそもそんなにバランス良くAndroidエンジニアとiOSエンジニアは集まらない
- 新規開発で仕様がふらふらしている中でプラットフォーム間で仕様合わせてくの割と大変(何にせよWeb版とは合わせないといけないのだけれど)
- リリース後に仕様変更があった場合にAndroidとiOSで修正内容に抜け漏れが起こってつらい(あまり細かい仕様書は書きたくない)
看板商品になるアプリ数個をひたすらメンテしていけばいい状況であれば、それぞれをネイティブで作ってメンテしていってもいいかと思うのですが(それでもアプリ数×2をメンテするのが現実的かというのはありますが)、アプリ数が5個や10個になってくると、モバイル部門がメンテするプロダクトの数自体はAndroid, iOSで合わせて10個20個と増えていくわけです。
人が足りないと嘆くのも一つの手ですが、私たちはエンジニアなので、エンジニアリングで何とかできる範囲は何とかしたい。
ということで、Xamarinが無償化したのも併せていいタイミングだろうというのもありますし、一昔前にクロスプラットフォーム開発ツールと呼ばれていたものも含めて、開発ツールに対して自分が持っている印象を書き連ねてみようと思います。
免責事項
- ところどころで断定的な書き方をしているところもありますが、Xamarinはほとんど使ったことがなく、今回のメモを作るためにググって得た知識がほとんどです。Xamarin周りの評価には大いに色眼鏡をかけて読んでください。
- 普段仕事をしている環境が、AndroidネイティブとCordova(React+Redux)なので、それ以外の分野については学習コストを高めに見積もる傾向があります。
- ツールのジャンル分けは雑です。筆者が現在認識している範囲の用途でしか書いていない場合があります。
ジャンル
- ネイティブ
- Android SDK, iOS SDK
- ネイティブ(自称)
- Xamarin, React Native
- HTML5ハイブリッド
- Cordova
ネイティブ
Android SDKやiOS SDKが提供するAPIを直接操作することで、モバイル端末に処理を行わせる開発方法です。
ライブラリなどにより高度にAPIがラップされている場合でも、同じ処理系(コンパイラ)を通るものや極めて高い互換性を持つものについてはこのタイプと見なします。
Android - 言語
Android Java
JDK1.6〜JDK1.8相当。Androidのバージョンによって対応するJava環境が異なる。
Retrolambda
JDK1.8の幾つかの文法が含まれるJavaソースコードをJRE6/AndroidJava環境で動作するバイナリにするための補助コンパイラ。
Kotlin
ScalaやGroovyなどに影響を受けた柔軟な文法や処理機能を持ちながら、Javaとは100%のバイナリ互換性で相互運用ができる。Androidとの互換性も高い。
Android - リリースエンジニアリング
Gradle
Android公式ビルドツール。AndroidアプリプロジェクトをAPKファイルにビルドする責務を持つ。Maven Central RepositoryやjCenterで配布されているライブラリをダウンロードするなどライブラリ依存性管理の役割も持っている。Android Studioのコンパイル処理も実態はGradle。
事実上一択です。mavenやsbtなどのビルドツール向けにもAndroidアプリをビルドするためのツールは配布されていますが、非公式である点からあまり信頼できません。
Gradleはインターネットに接続できるJVM環境下であればWindows/Mac/Linuxを問わず動作できるため、柔軟にCI環境を選択できます。他の選択はあまり必要になりません。
iOS - 言語
Objective-C
C言語から派生したやつ。C言語との互換性を保ったままオブジェクト指向プログラミングができるように拡張した結果、各種文法が奇妙な形になった。C言語派生だが、(mac版には)GCもある。
Swift
ScalaやGroovyやKotlinなどなど様々な言語からいいとこ取りをして生まれた。互換性のためにObjective-Cの仕様の面影が残ったものの、モダンな言語が備える機能の多くを持っている。Appleが公式にLLVMコンパイラを用意していることや、オープンソース化されたことなどから、今後は様々なプラットフォームで動作するようになる可能性がある。
iOS - リリースエンジニアリング
Xcode
Apple公式IDE。iOSアプリはGUIからアプリのビルドやデプロイを行うのがスタンダードらしい。
xcodebuild
Xcodeに付属のビルドコマンド。スタンダードから外れてCLI上でビルドを行いたい場合に用いる。しかし生々しいので難易度が高め。iOSでCIを始めようとした人々を絶望の淵へと誘う。
Bot
OS X ServerにXcodeをインストールした場合に使えるCIサーバー。ローカルのXcodeからリモートのXcodeをGUIで設定する。設定に冪等性を持たせづらいので、安定運用するノウハウを身につけるのが難しそう。 |
CocoaPods
ライブラリ依存性管理ツール。ライブラリプロジェクトのダウンロード、xcodebuildによるライブラリのビルド、アプリ本体のワークスペースとの依存関係の作成と更新などを行うため、.xcodeproj
や.xcworkspace
が変更される。Podfileでライブラリを管理する。
Carthage
ライブラリ依存性管理ツール。ライブラリプロジェクトのダウンロード、xcodebuildによるライブラリのビルドは行うが、アプリ本体への統合は行わない。Xcodeから開発者が好きな方法でリンクすればよいという思想らしい。ちなみにカルタゴと読む。読めない。
fastlane
主にiOSアプリについて、テスト、ビルド、デプロイを行うために生まれたタスクランナー(Android版もある)。xcodebuildのパラメータ付与や、CocoaPods/Carthageによる依存性解決、TestFlight/Crashlytics Beta/iTunes Connectなどへのデプロイ、チャットへの通知など、リリースエンジニアリングに関わる様々な処理をラップした関数を提供してくれている。
その他
何故かiOS界隈のツールはRubyが多い……
使い方によっては、Ruby関連の知識(Bundlerとか)もリリースエンジニアリングの範疇に入るかもしれません。
共通化ソリューション
J2ObjC
JavaソースコードをObjective-Cソースコードへと トランスパイル する。対象となるパッケージは多くはないが、OkHttp/RetrofitやGSONくらいなら動く。1月に1.0が出た。
Swift on Android
OSS版SwiftのビルドターゲットにAndroid NDKが入っている。"SwiftからJavaインターフェースを呼び出すことは理論上可能だが、SwiftコンパイラはJavaに対してブリッジを提供しない"とのこと。
Haxe
JavaとC++がターゲットにあるのでなんとかなるのでは(ならない)。
ネイティブ(自称)
言語・動作環境
Xamarin
.NET(C#, F#)のお作法でビジネスロジックが書ける。レイアウトの実装はStoryboardやLayout XMLなどのネイティブな方法で行うのが標準だが、典型的な画面であればXamarin.Formsにより共通レイアウトを作成できる。ネイティブAPIについては薄いラッパーしか用意されていないケースも多いため、良くも悪くもネイティブ開発のノウハウが必要となる。
React Native
ES2015のお作法でビジネスロジックを書ける。Reactのお作法でレイアウトが書ける。最小単位のコンポーネントである<View>
や<Text>
などの内部実装はAndroid/iOSそれぞれのネイティブコードになっており、RCTRootView.mやReactRootView.javaの上に描画される。fetch()
やsetTimeout()
などもネイティブモジュールとして用意されており、内部実装は各プラットフォームのネイティブコード。
比較すると
両者とも、Android/iOSに合わせて振る舞いを変えるViewコンポーネントは存在しますが、細かく制御しようとするとプラットフォームごとにレイアウトを作成することになるので、レイアウト実装の共通化は実現できないことが多そうです。
また、両者とも、画面描画はネイティブで行われ、ロジックの実行は非ネイティブのランタイム(JavaScriptCore, Monoなど)の上で行われます。
リリースエンジニアリング
react-native-cli
React Nativeのビルドコマンド。XcodeやGradleによるビルドの最中にJSコードのトランスパイルが入る |
xbuild
Xamarinのビルドコマンドらしい。
NuGet
.NET向けのパッケージマネージャ。Xamarinでも使えるらしい。
rnpm
React Native Plugin用のパッケージマネージャ。詳細不明。
CodePush
CordovaとReact Nativeに対応した、リリースと更新通知のサービス。実態はよくわからない。
感想
独自の世界観に基づき、独自のコンパイラの成果物をいい感じにリンクしてネイティブ環境で動作するようにしているだけなので、我々が調整できない場所でエラーが起きたりすると、どうにもならなくなる印象です。
HTML5ハイブリッド
動作環境
Cordova
www/
以下をいい感じにWebViewに乗せたアプリをビルドできる、ネイティブアプリのテンプレート。ネイティブ機能をブリッジすることでJSからJSライブラリを触るかのようにネイティブ機能を扱えるプラグインを作成することもできるが、ネイティブライブラリに比べると制約があるため開発はしづらい。
ビジネスロジックも描画もブラウザ上で行うため、JS側で重い処理を行うと描画処理が影響を受けます。
言語
ES2015
最新のJS。FirefoxやChromeへの実装が進んでいるが、まだ使えない機能も多いため、Babelなどを通してES5コードにトランスパイルして動作環境を確保することが多い。
TypeScript
型に守られたJS。ES2015の一部が実装されている(全てではない)。そのままではブラウザ上で動かないため、ES5またはES2015へのトランスパイルを行う。
リリースエンジニアリング
NPM
パッケージマネージャー。基本的にはライブラリ依存性管理を行うが、プロジェクトのメタ情報も扱うため、プロジェクト構成管理ツールのような役割も担っている(往時のMavenのようである)。ブラウザ上で動かないバイナリなども扱う。
Bower
ライブラリ依存性管理ツール。ビルドを伴わない静的に読み込むだけのライブラリを配布していることが多い。CSSやSassなどを配布していることもある。
Gulp
タスクランナー。プラグインを組み合わせていくことで、ファイルのコピーやコンパイル、結合などの順序を定義していく。
Browerify
JSコンパイラ。require('hogehoge')
でnode_modules/
内のライブラリを参照できるよう、JSファイルをコンパイルする。プラグインを入れることでコンパイルを色々と味付けできる。
webpack
静的ファイルまとめるマン。browserifyの責務に加えて、各種の静的ファイルをコンパイル&結合して、HTML(scriptタグ)から呼び出しやすい少数のファイルにまとめることができる。
CodePush
CordovaとReact Nativeに対応した、リリースと更新通知のサービス。実態はよくわからない。
ネイティブからは逃げられない
cordova build
を実行するとplatforms/
以下に完全な形でネイティブアプリのプロジェクトフォルダができあがるため、それ以降で扱うツールはネイティブと共通になります。
どんなときにどのツールを選ぶのか
評価の軸
開発の要件はプロジェクトによって様々です。重視されることが多い評価軸を挙げます。
- 開発スピード
- とにかく早期に動くものを出せること
- Android/iOSそれぞれに同等の機能が載ったアプリを提供できるまでの時間
- 堅牢さ(バグの起こりづらさ)
- 主に型付け
- よいフレームワークや設計があるかどうか
- レスポンス
- 200ms or die
- 保守性
- 修正対象の少なさ
- 修正対象の抜け漏れの発生しづらさ
- 経年劣化の起こりづらさ
- 学習コスト(開発)
- 開発に参加するのに必要な知識の量
- 学習コスト(リリースエンジニアリング)
- リリースエンジニアリングに必要な知識の量
評価は適当にA〜Eくらいで付けていきます。
開発スピード
PF | 評価 | 講評 |
---|---|---|
ネイティブ | D | Android, iOS向けにそれぞれ開発を行うことになるため、仕様合わせのコミュニケーションコストも勘案すると少し遅い。仕様の認識合わせが高水準でできていて、各PFに慣れたメンバーが開発する分には、さほど遅くならないかもしれない。J2ObjCでドメインモデルや通信ライブラリを共通化してしまうことで工数の短縮を行える可能性もある。 |
React Native | B | エミュレータ/シミュレータでのReloadが容易なので、トライアンドエラーの速度は多少見込める。ビジネスロジック部分はJSで書いたものがPF間で共通で動く。Viewも多くの部分で共通にできるが、AppBar/NavigationBarの部分などは分けたほうがいい場合もある。カスタムコンポーネントを作り始めると遅い。 |
Xamarin | C | ビジネスロジックはC#/F#で共通化できる。ViewはStoryboard/Layout XMLで書いてしまうとネイティブと同等のコストがかかるが、Xamarin.Formsのカスタマイズに留めれば共通で開発できる可能性もある。言語の堅牢さを加味するとJSに比べて開発速度は落ちるかもしれない。 |
Cordova | A | ブラウザ上でトライアンドエラーを行いやすいため、一定のレベルに到達するまでのスピードは非常に高い。また、デザインを共通にしていれば、ネイティブ機能以外の開発は完全に共通で行える。自作プラグインに手を出すと遅い。 |
堅牢さ
PF | 評価 | 講評 |
---|---|---|
ネイティブ | A | JavaとSwiftの評価。Obj-Cだとワンランク下がる。ネイティブSDKと枯れたライブラリの組み合わせで開発を行う限り、環境由来の不具合は起こりづらい。 |
React Native | C | BabelによるReact+ES2016。動的型付け言語という点は不安だが、PropTypesやFlowTypeで型付けの補助ができるのでカバーは可能。処理系が機種依存にならない点でCordovaよりは良い。組み込みコンポーネントにバグが有った場合に対処できない。 |
Xamarin | A | C#/F#とMonoを信じる心。フェンリル社が紅白歌合戦アプリに使っているMvvmCrossとかを使うと堅牢さ++。 |
Cordova | D | Web開発と同等のつらみを抱える。React+Redux, TypeScript, Crosswalkなどを(多大な保守コストを犠牲にして)最大限活用すればB評価程度の堅牢性は確保できるかもしれない。 |
レスポンス
PF | 評価 | 講評 |
---|---|---|
ネイティブ | A | ネイティブなViewとネイティブな処理系。もちろん早いしパフォーマンスチューニングもしやすい。 |
React Native | A- | ネイティブなViewとJSインタプリタ経由の処理系。操作のレスポンスは良いが、重い内部処理を行う場合の速度には疑問がある(未検証)。 |
Xamarin | A- | ネイティブなViewとMonoフレームワーク経由の処理系。操作のレスポンスは良いが、重い内部処理を行う場合の速度には疑問がある(未検証)。 |
Cordova | E | WebViewの特性上、レスポンスが高確率で200ms以上遅れる。重い内部処理を行う場合の速度には疑問がある。 |
保守性
PF | 評価 | 講評 |
---|---|---|
ネイティブ | B | SDKの後方互換性が確保されている範囲では、バージョンアップによる不具合が起こりづらい。同じ修正をPFごとに施す際に、本当に同じ挙動になっているかどうかの確認にかかるコストが高め。ただし、他のツールで共通化が為されていたとしても、品質管理チームの確認対象が共通化されるわけではない点を加味すると、大きな問題ではないのかもしれない。 |
React Native | D | メンテナンス時のバグの出やすさについては、JSという点でまず低め。共通化できる部分が多い点やReactである点は良いが、若いプロダクトなのでBraking Changeが来やすい。 |
Xamarin | A | 共通化できる場所が多くて、静的型付け言語の中でも評価の高いC#/F#で記述していて、MSのご加護を受けているXamarin様に、保守性の問題などあろうか。ビルドプロセスが謎なことだけが心配。 |
Cordova | C | メンテナンス時のバグの出やすさについては、JSという点でまず低め。あとはWebフロントエンド界における普通レベルの保守性は確保できる。Reduxでアーキテクチャを組んでおくと修正のしやすさは上がるのだが、バージョンアップ時の不具合の影響範囲が大きそうでアレ。 |
学習コスト(開発)
PF | 評価 | 講評 |
---|---|---|
ネイティブ | C | Android Java + Android SDK, iOS SDK + Swiftについて学んでおく必要がある。両方の開発に参加する場合はコストが倍プッシュ。 |
React Native | B | ES2016がわかっていれば一応OK。ネイティブコンポーネントの挙動を追いかけようとすると、ネイティブのView周りの知識が必要になる。 |
Xamarin | D | C#, Android SDK, iOS SDKについて学んでおく必要がある。ビジネスロジックだけならC#だけでよいが、何かネイティブな機能を利用する場合にはほぼSDKがそのまま露出しているAPIを利用しなければならない。場合によってはAndroid Layout XMLやStoryboardを触ることになる。 |
Cordova | B | 基本的にWebフロントエンドの知識のみでOKだが、プラグインの挙動を追いかけ始めた瞬間にネイティブ開発+Cordovaプラグインのお作法を知識として求められる。 |
学習コスト(リリースエンジニアリング)
PF | 評価 | 講評 |
---|---|---|
ネイティブ | A | Androidは素直なGradleなので変なツールを組み合わせなければ大丈夫。iOSは自動化し始めるとつらいけどXcodeをポチポチしているうちは平和。 |
React Native | B | ビルドプロセスにブラックボックスが多いこともあり、難を抱えやすい。とはいえ、ネイティブ部分は比較的素直なネイティブプロジェクトなので、ネイティブ知識が充分にあれば大丈夫。 |
Xamarin | D | .NET界(Mono界?)の知識が大きなウェイトを占める(DLLファイルとか出てくる)。ネイティブ知識も所々で必要になる。ブラックボックス多め。 |
Cordova | C | Cordova界とネイティブ界とNode界の全てのリリースエンジニアリングを制覇しなければならない点ではEを付けたいが、platforms/ 以下のネイティブコードも読めば読めないことはない点でブラックボックス感はXamarinよりはマシということで。 |
使い分け
指標 | ネイティブ | React Native | Xamarin | Cordova |
---|---|---|---|---|
開発スピード | D | B | C | A |
堅牢さ | A | C | A | D |
レスポンス | A | A- | A- | E |
保守性 | B | D | A | C |
学習コスト(Dev) | C | B | D | B |
学習コスト(Release) | A | B | D | C |
分かってたけど、どの開発ツールでも何かしらの良し悪しはあるなあと。
- ひたすらスピードを求められるプロトタイピングなどはCordova(Ionicなど含む)
- Cordova上でReactで開発した資産を引き継ぎつつユーザーレスポンスを改善するならReact Native
- 時間がかかってもいいからとにかく安定させたいならネイティブ
- 学習コストが高くてもよくて開発スピードと安定性を両立するならXamarin
こんなところでしょうか。
まとめ
書き始めた当初に持っていた印象に近いマトリクスができたので、書いてよかったと思います。
上手くいけばXamarinよさそうだけど、C#ってJavaやSwift以上に社内で孤立するのでは・・・でも人材市場の広さという点ではJavaと並んで探しやすそうな気もする・・・。
React Nativeは安定性への不安がなくなってくれば、Reactを使っているWebチームと人材のコンバートをしやすくなったりJSライブラリを共有できたりして、現状と地続きでメリットが大きくなるのですが・・・。
今後も定点観測していきたいです。
余談: Xamarinの開発環境
Xamarinで開発する場合、MacでもXamarin Studioがあるから開発できないことはないんだけど、WindowsでVisual Studioを使ったほうが開発効率が上がりそうなので、設備投資を伴うのではという懸念がすごくあります。
と思いましたが、識者2名に聞いてみたところ、Macで開発しているそうです。
@nkzn @amay077 Xamarin Studio for Mac使ってます。VSの接続周りで詰まって辛いとかビルドにかかる時間とかを考えると、多少アレでもMacのほうが総合的には効率いいです。将来的にはRiderで状況改善すると良いなと思ってます。
— いせ (@iseebi) 2016年4月19日
@Nkzn Macですね。VSの高生産性は分かりつつもAndroid開発だとドライバ不要,ADBコマンド周りの便利ツールが充実してる等、VS以外のやりやすさがMacの方が上です。iOSに関しても @iseebi さんの言われる通りですね。
— 3度目S+のあめいスピナーデコ (@amay077) 2016年4月19日