React Nativeハイブリッドアプリの作成秘話

 [NAVITIME JAPAN Advent Calendar 2019](https://qiita.com/advent-calendar/2019/navitime_japan) もちょうど折り返しに入りましたね。    
iOSをメインにReactNative/AndroidエンジニアをやっているNUMAです。   
今日はReact Nativeハイブリッドアプリを開発したときのお話をいたします。   
React Nativeの導入を検討している方の参考になれば幸いです。

## はじめに
React Nativeとは、Facebook社が2015年にリリースしたJavaScriptフレームワークです。 "Learn once, write anywhere."という謳い文句で、クロスプラットフォーム開発において大きな存在感を出しています。大きな特徴としてCallstackやExpo、Microsoftなどといった世界中の様々な企業や個人からコントリビュートされていることが挙げられます。弊社では2018年よりReact Nativeを導入し開発を行ってきました。今日はこれまでの開発の中でとくに工夫が必要だったハイブリッドアプリの話をいたします。

## 採用理由
ハイブリッドアプリの話を始める前になぜReact Nativeを採用したのかについて触れておこうと思います。
### 1. リソースの共通化
やはりこれが一番大きな理由です。私が所属していたチームでは少人数（Android/iOS各1名）でたくさんのアプリ(6アプリ×2OS)の開発・運用を行う必要がありました。ソースコードの共通化が可能になることで、開発の必要なものが大幅に減らすことができます。
### 2. 開発の高速化
プロダクトが大きくなってくると問題になるのがビルドタイムです。特に、開発終盤のデザイン調整のシーンでビルドタイムは大きな問題になります。React NativeはHot Reload機能を提供しており、ビルドタイムを大幅に短縮することが可能です。最近ではネイティブ開発でも優秀なプレビュー機能が提供されていたりと、ビルドタイムの問題の解決策が提示されています。しかし、2018年当時にはそれら機能は提供されておらず、我々のチームにはReact Nativeが非常に魅力的に感じました。
### 3. リリースの柔軟性
MicrosoftのCode Pushというサービスを利用することで、Appleの審査無しでアプリに変更を加えることが可能になりました（ただし、Apple Store Reviewガイドラインに準拠した変更のみ）。これにより、リリース後に発覚したレイアウトの不具合や機能の変更を伴わないバグ修正などがCodePush経由で行えるようになります。

## ハイブリッドアプリという選択
これから本題のハイブリッドアプリについてお話していきます。そもそもここで言うハイブリッドアプリとは、React Nativeをネイティブアプリの一部として利用する方法です。基本的にReact Nativeアプリではネイティブコード（SwiftやKotlin）を書く必要はありません。どうしてもネイティブ側で処理する必要がある場合ネイティブコードの実装が必要になります。ただし、ネイティブ層とReact Native層での連携にはそれなりに学習コストがかかります。また、ネイティブの知識が必要になるため、JavaScriptは書けるがプラットフォームについては詳しくないという人にとっては少々酷かと思います。しかしながら、我々のチームではハイブリッドアプリを開発する方針に決定しました。理由は以下の2点です。
#### 1. NAVITIMEの地図を描画したかった
React Native Communityが[react-native-maps](https://github.com/react-native-community/react-native-maps)という地図フレームワークを提供しています。しかし、弊社では地図画面を内製しており、その地図を利用したいという背景がありました。そのため、react-native-mapsと同等の地図フレームワークを内製するか、ネイティブ側で実装している地図画面を呼び出す仕組みを作る必要がありました。
#### 2. Native UI Componentを作れる地図エンジニアがいなかった
react-native-mapsと同等のものを作成するにはあまりにも工数が不足していたという背景があり、ネイティブ側で実装した地図画面をReact Native側から呼び出すという方針で実装を進める事になりました。実はこの選択も決して楽なものではありませんでした。それについては後述の[大変だったこと](##大変だったこと)にて説明いたします。

## ネイティブ開発と比べてよかった点
### 開発量の削減
開発量が半分になることはありませんでしたが、1/3から1/4程度コードを減らすことができました。現状、半分程度の画面がReact Nativeにより実装されており、React Nativeの割合が上がっていけばさらにコード量を削減できます。これは期待していた通りの結果で、今後もReact Nativeを採用し続けるにたる理由になっています。
### 開発リソースの柔軟性が向上
リソースの共通化がされることで開発リソースの柔軟性が格段に向上しました。これは、共通リソースの存在により、各エンジニアの担当できる範囲が広がったことが理由です。これまでは片方のOSが忙しくても、もう一方のOS側からリソースを融通してもらうことはできませんでしたが、OS間の垣根を超えてタスクの共有が可能になりました。これは、当初想定していたよりも大きなメリットとなりました。

## 大変だったこと
最も困難だったのがReact Nativeとネイティブ間の画面遷移を制御する機能の実装です。通常のReactNativeアプリでは単一のReact Native画面内で画面遷移を行っています。しかし、ネイティブ画面も介することで、複数のReactNative画面を管理する必要が出てきます。そこで、当時既に開発が終了していたAirbnbの[native-navigation](https://github.com/airbnb/native-navigation)を参考にReact Nativeとネイティブ間の画面遷移を可能にする機能を実装しました。   
概念としてはネイティブ層で用意したナビゲーションスタックを利用し、画面の管理をネイティブ側で行う仕組みです。コード量はそこまで多くはありませんが、ネイティブ層とReact Native層でイベントがそれぞれ存在しているため、シーケンスが複雑になり、非常に苦戦しました。

## 結局ハイブリッドアプリはおすすめなの？
基本的にはReact Nativeのみで開発することをおすすめします。ただし、下記条件が当てはまれば、実装の複雑さは多少あるものの、React Nativeの恩恵を受けることができると考えています。そうでない場合はおすすめできません。
- 両OSのネイティブエンジニアがチーム内にいる
- 対象のアプリに複雑な画面遷移がないこと


## まとめ
React Nativeハイブリッドアプリの開発についてお話しました。   
リソースの共通化やHot Reloadなど開発が楽になる要素がいくつもあり、その恩恵を受けるために結果トリッキーな実装を行いました。まだ、100%おすすめということではありませんが、一つの選択肢にはなると感じています。