React Native製アプリのクオリティを上げるために工夫した事 | by TAKUYA | 週休7日で働きたい

 
Illustration by unDraw
InkdropというMarkdownノートアプリを一人で作っているTAKUYAです。最近、React Nativeを使って、iOS版とAndroid版の新しいバージョンをリリースしました。React Nativeは、JavaScriptとReactを使ってクロスプラットフォームなモバイルアプリが開発できるフレームワークです。
どうすればReact Nativeでハイクオリティなアプリが作れるのか、今回の開発を通して多くのことを学びました。本稿では、よりよいアプリを作るために自分が工夫したことをシェアします。既にReact Nativeでアプリを作っている方も、これから作ろうと思っている方も参考になるかと思います。

概要

  • OSSライブラリは慎重に選ぶ
  • ネイティブ拡張モジュールは出来るだけ使わない
  • UIテーマの対応
  • タブレットの対応
  • 動作を軽く保つ
  • 違和感のないスプラッシュスクリーンを作る
  • CodePushは使わない方が良い

OSSライブラリは慎重に選ぶ

iOSのUIKitと違って、React Native自体はクオリティの高いUIや画面遷移を作れるモジュールを提供しません。なぜならこのフレームワークは、ReactとJavaScriptを使ってUIのレンダリングやデバイスAPIへのアクセスを提供することに注力しているからです。なので、イメージ通りのクールなUIを作ろうと思うと沢山の労力を伴います。
しかしながら、そんな問題を解決してくれる様々なライブラリがOSSで公開されています。これらを上手く活用することで、手間を掛けずにプロフェッショナルなものが作れます。以降より、Inkdropで使用したライブラリをご紹介します。

ナビゲーション (画面遷移)

上記画像をご覧の通り、アプリにはサイドバーがあったり、モーダル画面やスタック状の画面遷移が見られます。
これらのルーティングやナビゲーションを実現するために、react-navigationを使いました。これを使えば簡単に柔軟なルーティングや滑らかな画面遷移を導入できます。注意点は、モーダル画面に対応していないことです。そこで react-native-modal を併用します。これでアニメーション付きでカスタマイズ可能なモーダル画面を導入できます。
これらのライブラリは特にこだわりが無い限りかなりおすすめです。もしすごく凝ったことがしたいなら、こちらの記事が参考になるでしょう

高品質かつクロスプラットフォームなUIコンポーネント

iOSとAndroidではUIのガイドラインが異なります。それぞれのプラットフォームのスタイルに適合したボタンやテーブル、ヘッダなどを自前で作っていてはキリがありません。世の中には見た目の良いコンポーネントが沢山公開されていますが、これらをほいほいアプリに導入するのも考えものです。アプリが徐々に肥大化して不安定になるからです。
NativeBase
そこでオススメしたいのが、NativeBaseです。これはReact Native向けに作られた、クロスプラットフォームに対応したUIコンポーネント群を提供するライブラリです。React Native版 Bootstrapと言えば分かりやすいでしょう。NativeBaseはデザインの良いコンポーネント群だけでなく、レイアウト系のコンポーネントも含んでいるのが便利なポイントです。コンポーネントは自動でプラットフォームに合わせて切り替わるので、心置きなくアプリ作りに集中できます。

ネイティブ拡張モジュールは出来るだけ使わない

React Nativeはまだまだ未成熟の技術なので、APIが頻繁に変わります。そしてバージョンを上げるたびにどこかが壊れます。もし問題がネイティブ側に存在したら、それを解決するのは至難の業でしょう。それはライブラリの作者にとっても同じです。なぜなら:
We found that most React Native Open source projects were written by people who had experience with only one or two. — Airbnb
React Nativeのオープンソースプロジェクトは、1つか2つしかプラットフォームの開発経験を持たない人が作ったものだ — Airbnb
つまり、彼らは必ずしも全てのプラットフォームに精通している訳ではないのです。僕もReact Native用のSQLite3のプラグインを作って公開していますが、iOSとAndroidの両方をメンテするのは大変です。コントリビュータがWindows対応も増やしてくれましたが、僕はそちらのことは全くわからないという状態です。
もしネイティブ拡張をするライブラリをインストールしようと考えているのなら、これらのことを念頭に置いてください。僕は結局、デバッグのために頻繁にネイティブコードを読む必要がありました。この苦しみは、ネイティブ拡張を出来るだけ避けることで軽減できるでしょう。
以下は、Inkdropが使用しているネイティブ拡張ライブラリの一覧です:
より少ないネイティブ拡張依存は、あなたのアプリをよりメンテしやすくして、React Nativeの将来のバージョンに適応しやすくします。

UIテーマの対応

React NativeでUIのテーマに対応するのは中々チャレンジングです。なぜなら、それはViewのレンダリング方法に大きく左右されるからです。iOSではAppearance Proxy (UIAppearance)が提供されていて簡単に標準コンポーネントの見た目を変えられますが、React NativeにはそのようなAPIは提供されていません。自前で用意する必要があります。
幸い、NativeBaseはテーミングに対応しています。以下のように変数を定義することで、NativeBaseのコンポーネントの見た目をカスタマイズ出来ます:
しかしこれだけでは不十分で、NativeBase以外のコンポーネントの見た目は依然切り替え出来ません。そこで、react-native-extended-stylesheetを採用しました。これは、次のようにStyleSheetで変数が使えるようにしてくれるライブラリです:
簡単ですね。これで見た目を動的に切り替え出来るようになりました!
注意: NativeBaseはStyleProviderがスタイルをキャッシュしているので、テーマを適用するにはアプリを再起動する必要があります。

タブレットの対応

例えばタブレット用に2カラムのレイアウトを表示したいときは以下のようにします:
しかしながら、画面サイズに応じてレイアウトを切り替えるにはそのままでは問題があります。というのも、iPadで Split ViewSlide Over でアプリを動作させた時に、 常に画面全体のサイズを返してしまうからです:
知りたいのは画面全体のサイズではなくアプリ領域のサイズです。それを取得するには、アプリの最も外側に一枚Viewを敷いて、 をスタイルに指定します。そしてそのViewの イベントでビューのサイズを取得するのです。そのサイズをReduxのStoreなどに記憶しておきます。
こちらがコードのスニペットです。ご参考ください(英語):

動作を軽く保つ

PureComponentを使う

アプリの完成度が高まるにつれて、必ずどこかでパフォーマンスの調整が必要になります。React NativeはReactでUIを描画しますので、Reactのパフォーマンス最適化手法がほぼそのまま使えます:
アプリを軽快に保つための基本的な手法としては、 を使って無駄なレンダリングを阻止する方法です。さらに を使えば自動で を監視して、それが変化したときだけレンダリングするように計らってくれます。僕は個人的に Higher-Order Components(HOC) パターン を採り入れているので、recompose を使用して同様の事をしています。

Render()内でコールバック関数を作らない

PureComponentを使ってビューを構成していたとしても、気をつけるべき点があります。以下の例を見てみましょう:
この のレンダリング時に プロパティに対してコールバック関数が渡されていますが、処理が実行されるたびに新しい関数が作られてしまっています。すると、 がたとえPureComponentであっても、毎回異なるコールバック関数が渡されるので、レンダリングがスキップされずに処理が重くなってしまいます。この問題を避けるには以下のように記述します:
もしリストが長くなるようなら、FlatList を使用しましょう。

違和感のないスプラッシュスクリーンを作る

JSロード中に真っ白な画面になるのを防ぐ
React Nativeで起動画面をセットアップした人は分かるかもしれませんが、React Nativeのアプリ実体であるJavaScriptコード本体の読み込み時に真っ白な画面が一瞬表示されます。特に白以外の背景を持つアプリにとっては強い違和感を与える現象です。
この問題の対処には以下の記事を参考にしました。この記事は上記画像のように、JSのロード中でも画面が真っ白になるのを防ぐ起動画面をセットアップする方法が丁寧に解説されています。めっちゃ有用です:

CodePushは使わないほうがいい

CodePush はアプリを審査を通さなくてもアップデートできるようにする仕組みです。CodePushを使えば、軽微なバグ修正などを素早く現行のアプリに適用出来ます。
しかしちょっと待ってください。まずApp Storeのレビューはもう既に充分短いです。昔は平気で2週間ほど待たされたものですが、今では平均で2日以内で審査が完了しています。よほど逼迫していない限り、ビジネスに大きな影響は無いでしょう。また、CodePushはネイティブ拡張を伴うライブラリです。先に述べたように、アプリを安定してシンプルで簡潔に保つためには出来るだけ使用を避けたいものです。
以上が、拙作アプリをよりよくするために工夫したことでした。参考になれば幸いです!
この記事をお読みくださりありがとうございます。僕はフリーランスをしながらアプリを作っていて、それだけで食っていこうと日々奮闘している者です。その過程をブログに書いていますので、ぜひ他の記事も読んでみてください!