React Nativeによるクロスプラットフォームモバイルアプリ開発の所感

PWAについて

PWA(Progressive Web Apps)はWebサイト上のコンテンツをPCやスマホにインストールしてアプリケーションのように動作させる仕組みです。Webサイトとして構築したコードをそのままアプリケーションとして流用できるため開発コストが少なく、AndroidとiOSの両OSで利用できます。
ただし、利用するブラウザやプラットフォームによって動作環境が異なるという難点もあり、特にiOSはAndroidと比べるとサポートしている機能がいくつか制限されているため、必要な機能全てをPWAで賄えない可能性があります。(バナー表示・Push通知等)

特にPush通知は今日のモバイルアプリで通知を行う手段として必要不可欠なものになっていますが、iOSのPWAがPush通知に対応するのは2023年とのことです。(以下のページの「Web push notifications」を参照)
参考:apple.com

よって、要件がある上でPWAを用いて開発を行った場合に、Androidでは要件を満たせてもiOSではプラットフォームの制約で要件を満たせない事があります。そのような場合にはネイティブアプリとして開発を行う必要があります。

クロスプラットフォームモバイルアプリケーション開発フレームワークについて

しかし、AndroidとiOSの両プラットフォーム向けにネイティブアプリを開発する際にAndroid SDKとiOS SDKで別々に開発を行っていると、2つのプロジェクトを同時に進行・保守する必要があるため、多大なコストを要します。
そこで、目をつけたのがクロスプラットフォームモバイルアプリケーション開発フレームワークです。これを用いることで、Android, iOS両方に対応したネイティブアプリの開発を、1つのコード、あるいは大部分は共通のコードで記述して各モバイルプラットフォームに依存する必要最低限の部分だけを別々のコードで書くことによって行うことが可能です。
これによって、少ない時間とコストでAndroid, iOS両方に対応したネイティブアプリを開発することが出来ます。

このようなフレームワークとして有名なものの一例としては、.NET MAUI(旧Xamarin.Forms)、Flutter、React Nativeなどがあります。
今回はReact Nativeについて調査しました。

React Nativeについて

React Nativeは、Facebook社製の著名なWeb開発用フレームワークであるReactをベースとした、クロスプラットフォームモバイルアプリケーション開発用フレームワークです。
React Native

Reactをベースとしているため、アプリケーションのコードの全体・あるいは大部分をJavaScript/TypeScriptで記述できます。もし、Android, iOS固有のAPIを利用する場合は、各プラットフォーム向けの最小限のコードをAndroid SDKあるいはiOS SDKで記述します。このコードを元に、Android, iOS向けのネイティブアプリのバイナリファイル(Androidで言うAPKファイル)をビルドすることが出来ます。
さらにReact Native for Webを用いることでWebアプリとしてビルドし、PWAに対応させることも可能です。
React Native Components and APIs on the Web

React Nativeでの開発で利用できるサードパーティライブラリは充実しており、 npmで公開されているものを利用することができます。また、React Native DirectoryなるWebサービスから、React Nativeでの開発で利用可能なものを探すことが出来ます。
React Native Directory
ライブラリによっては特定のモバイルプラットフォームのみをサポートしているものがありますが、React Native Directoryでは対応しているモバイルプラットフォームによってライブラリをフィルタリングを行うこともできます。後述のExpo Goに対応しているかどうかもフィルタリングが可能です。

iOSにおいて、PWAでは対応できないとされていたPush通知を実現するライブラリも用意されています。
react-native-notificationsPush Notifications Overview

React Nativeの採用実績は多く、Facebook, Microsoft Office, Pinterest, Skype, Discordなどの有名なモバイルアプリの開発で用いられています。(2022/11/09現在)
React Native Showcase

開発ツールとExpoについて

React Nativeには開発ツールとしてReactNative CLIとExpoの2つが用意されています。

ReactNative CLIはReact NativeのプロジェクトにiOS/Androidといったプラットフォーム独自の機能やAPIに直接アクセスするするコードを追加することができるため、各プラットフォーム固有の高度な機能やAPIを使用したり、特定のモバイルプラットフォームで発生した問題を解消することができます。
React Native Environment Setup

但し、ビルドして実機上で動作確認し、リリースするにはiOS SDK+XCode環境やAndroid SDK+Android Studio環境が必要になります。特にiOS SDK+XCode環境はmacOS特有のものであり、その開発環境を導入するために Mac PCの購入とApple Developers Programへの加入が必要になります。また、各OS毎に異なるビルド設定を管理する必要があり、React Nativeのリリースが高頻度で行われたり、各プラットフォーム特有のビルド設定を最新のものにする必要性を考えると、保守するコストは非常に大きくなると予想されます。

ExpoはReact Native CLIで要求されるような煩雑なビルドフローを請け負ってくれます。
Expo

Expoの開発ツールであるExpo CLIの動作に必要なのはNode.jsとGitのみであり、アプリのコードはJavaScript/TypeScriptのみで記述されるため、Node.jsとGitがインストールされており、VSCodeなどのNode.jsに対応したエディターが利用できる任意の環境で開発が可能です。iOS向けのCocoaPods、Android向けのGradleによるビルド設定も基本的には不要です。また、アップグレードの作業もReact Native CLIと比べると容易です。

更に開発したアプリの実機上の動作確認は、Expo Goアプリをモバイル端末にインストールしてそこから開発中のアプリのQRコードを読み込むか、Expo Goアプリにログインしておいて自動的に検知したものを選択するだけで可能です。iOSにおいて、Expo Goを通じた動作確認だけであればMac PCもApple Developers Programへの加入も不要です。
また、EASなるサービスを用いてアプリのバイナリ(apk, ipa形式) をビルドし、 Apple App StoreとGoogle Play Storeに提出することも可能です。(iOSアプリのバイナリビルドと提出には Apple Developers Programへの加入、Androidアプリの提出にはGoogle Play デベロッパーアカウントが必要になります)
Expo Application Services (EAS)

結果として、ExpoとExpo Goを用いることでAndroid/iOSで動作可能なネイティブスマホアプリケーションの開発を、任意のOS上で進める事が可能です。特に、iOSアプリの開発をmacOS以外の環境で行えることは非常に大きいです。

但し、Expoで記述できるのはJavaScript/TypeScriptのコードのみであり、各プラットフォーム特有のネイティブのコード(Swift, Objective-C, Java, Kotlin等)を書けないため、各プラットフォーム固有の機能やAPIに深く踏み込めないなどのデメリットもあります。特に特定のプラットフォームのみで不具合が生じた際に、Expo SDKや依存ライブラリのアップグレードでも修正できなかった場合、プラットフォーム特有のコードによるワークアラウンドが導入できなくなります。
Expoのワークフローを標準のワークフローであるManaged workflowからBare workflowに移行することで各プラットフォーム固有の機能を用いることができるようになり、Bare workflowでしか導入できない一部のサードパーティライブラリも導入できるようになります。しかし、Bare workflowでは、iOS・Android特有のビルド設定を管理・保守する必要がある上に、XCodeとAndroid Studioといったネイティブの開発環境も必要になります。
Expo Workflows

この2つのどちらを導入するかはトレードオフになりますが、可能な限りManaged workflowを用い、不具合修正はExpo SDKやサードパーティライブラリのアップグレードで試みるようにしたほうが良いと思われます。

React Nativeを使ったTODOアプリケーションの開発

WindowsによるExpo開発環境を整備し、いくつかのライブラリを用いて、TODOアプリを作成してみました。
また、そのアプリの動作をExpo Goアプリを通じてAndroid, iOS端末、ならびにWebで確認しました。

Expo開発環境
  • Windows 10 Pro
  • Node 14.18.0
  • Yarn 1.22.15
  • Expo CLI 6.0.6
  • Expo SDK 46
  • TypeScript 4.6.3
  • Visual Studio Code 1.72.2
使用した主なライブラリ

AndroidだとActivityが画面の単位になっているように、React Nativeでも画面の単位を定義したり、画面遷移のやり方やナビゲーション・ルーティングの設定を行う必要があります。このような設定をしてくれるのがReact Navigationです。

また、UI構築にはWebだとBootStrapやTailWindCSSのようなフレームワークを用いるのが一般的です。React NativeではReact Native Material Kit、React Native Elements、Lottie、UI Kitten等が代表的ですが、今回はNative Baseを使用しました。

今回作成したTODOアプリの仕様

  • 認証
    • Googleアカウントでログイン・ログアウトが可能
    • Firebase Authenticationを使用
  • TODO管理機能
    • ログインしたユーザは「追加」「一覧を表示」「既存TODOの編集・削除」が可能
    • TODOの保存データベースとしてはFirestoreを用いる。

アプリ開発の前準備

1 動作確認用の実機端末(Android, iOSのいずれかあるいは両方)を用意します。
2 Expoのアカウントを作成します。
3 動作確認用のAndroid, iOS端末にExpo Goアプリをインストールします。
4 動作確認用のAndroid, iOS端末を開発用PCと同一のLANに接続します。
5 上記端末のExpo Goアプリ上で、Expoのアカウントでログインします。
6 Expo CLIを導入します。
7 Expoによる新規アプリケーションを作成します。

`$ expo init ${プロジェクト名}` コマンドで新規Expoアプリをセットアップします。

  • ここで使用する言語(JavaScript, TypeScriptのいずれか)を選択することができ、コードのテンプレートも生成してくれます。
  • 生成されたアプリのpackage.jsonには、Expoで開発を進めるために必要な npm パッケージ(expo, react, react-native, react-native-web 等) が自動的に dependencies と devDependenciesに追加されています。
8 `$ expo login` コマンドで先ほど作成したExpoのアカウントにログインし、Expo CLIにExpoアカウントを紐づけます。

アプリ開発の手順

プロジェクトのコードを書いて保存し、プロジェクトのルートディレクトリでコンソールを開き、 `$ yarn start` と打ち込みます。
すると、アプリのコンパイルが行われ、成功したらコンソール上にQRコードが表示されます。

その上で、以下のいずれかの手続きで、Expo Goにアプリをインストールします。
QRコードをExpo Goアプリ(iOSの場合はカメラアプリ)でスキャンします。
(Webについては、 `$ yarn start` した状態で、wキーを押すと、 ブラウザ上から`http://localhost:19006` で開けるようになります。)

また、コンソール上で rキーを押すと、自動的にリロードされます。
`$ expo upgrade` コマンドでExpo SDKをアップグレードすることができ、同時に React, React Native, React Native for Webなどの関連ライブラリもアップグレードされます。
ただし、依存している全ての npm パッケージが自動的に更新されるわけではないので、その場合は手動でアップグレードする必要があります。

完成したTODOアプリ動作の様子

Expo Goを通じて、開発したTODOアプリをAndroid端末にインストールします。
そのアプリでGoogleアカウントを用いてログインすると以下のようにTODO一覧が表示されます。

また、TODO一覧にある各3つのボタンの役割は以下のようになっています。

右下にある①「追加ボタン」をタップすると、TODO追加画面が表示されます。
TODO追加画面から、TODOのタイトル・説明文・締め切りを設定することができます。締め切りは日時選択UIを用いて設定することが可能です。

TODOの追加処理を行うと、TODOの内容がFirestoreに追加され、その処理が終わったらTODO一覧画面に遷移します。そのTODO一覧画面には先程追加したTODOが表示されています。

TODOの項目内にあるにある②「編集ボタン」をタップすると、TODO情報の編集用のモーダルが表示され、既存のTODOの内容を編集を行うことができます。

また、TODOの項目内にある③「削除ボタン」をタップすると、既存のTODOを削除することができます。
iOS端末にインストールすることも可能です。Expo Goを通じての動作確認をするだけであれば、Apple Developers Programに登録する必要もありません。
動作確認に使用したのはiPhone SEで横幅の解像度が小さくなりますが、native-baseでレスポンシブレイアウトを構築することにより、TODO一覧の項目のレイアウトは端末の横幅に応じて自動的に変化するようになっています。(「作成日」・「締め切り」の項目が縦並びになる。)

Webアプリとしての動作も確認可能です。

Webに関しては、締め切り設定の機能を提供する日時選択UIコンポーネントである react-native-modal-datetime-picker が Webに対応していないため、TODOの新規作成・編集処理はできませんでした。

個人的な所感

良かったと感じた点

AndroidとiOSに対するモバイルプラットフォーム固有の記述をほとんど書くことなく、TypeScriptのみでクロスプラットフォームモバイルアプリを作成することができました。一部のプラットフォームのみを対象としているUI要素(react-nativeのStatusBar等)がありましたが、他のプラットフォームでの挙動には影響しませんでした。
またCocoaPodsやGradleといったモバイルプラットフォーム特有のビルドシステムに一切触れる必要がありませんでした。
さらに、Expo GoアプリでQRコードをスキャンあるいはExpo GoアプリのDevelopment serverからアプリを選択すると、任意のモバイル端末にExpoで開発しているアプリがインストールされて即座に動作確認が可能でした。
加えてrキーをタップしたら変更内容がホットリロード機能によって即座に実機に反映され、挙動の変化を見ることができたおかげで、高速に開発を進めることができました。

Expo SDKのアップグレードは `$ expo upgrade` コマンドを打ち込むのみであり、同時にExpoアプリが依存している react-native や react-native-web などの関連 npm パッケージもアップグレードされるため、容易にアップグレードが可能であると感じました。
またターゲットとするモバイルプラットフォームに応じて利用可能なサードパーティライブラリを React Native Directory から検索することができ、要件を満たす使用するサードパーティライブラリを事前に調べることができました。
lodashなどのJavaScript Utility Libraryも利用可能です。

注意する必要があると感じた点
Androidで正常に動作してもiOSでは正常に動作しないという状況に遭遇したため、動作確認はサポートする各モバイルプラットフォームの実機でやる必要があると感じました。
開発中、TODO一覧におけるTODO編集用モーダルがAndroidとWebだと正常に表示されますが、iOSだと以下に示したように表示崩れしたのを確認しております。

今回はたまたま、native-baseを用いる部分のDOM構造を変更することで修正できたが、毎回この方法のみで修正できるとは限りません。そのため、ライブラリのGitHubページにある issueやPRは念入りに目を通し、品質が十分であるかを確認する必要があると感じました。場合によっては、Bare workflowへの移行も検討する必要があると考えています。
`$ expo upgrade` コマンドを実行した後、React Nativeのバージョンと依存するライブラリのバージョンの組み合わせによっては、コンパイルが通らないことがありました。

また、既存のnative-baseのバージョンのコンパイルが通らなかったため、native-baseを最新のバージョンに上げる必要がありました。
基本的に、 `$expo upgrade` で同時にアップグレードされなかったライブラリは、バージョンごとの互換性をライブラリのページなどで念入りにチェックした上で、手動でアップグレードすることになります。
Expo SDKのアップグレードガイドにも目を通しておくと良いかと思われます。

使用しようとしているサードパーティライブラリがサポートしているプラットフォーム(Android, iOS, Web, Expo Go)を導入する際、開発中のアプリがサポートする想定のプラットフォームと互換性があるかを予め確認する必要があります。実際、日時選択UIを提供するために使用したnpmパッケージである「react-native-modal-datetime-picker」はAndroid, iOSのみを対象としており、Webには対応していませんでした。
特に、当初はAndroid, iOS向けのネイティブアプリのみをターゲットとしてリリースする予定だったものををWebアプリとしてリリースする可能性がある場合、ライブラリの導入段階で依存しているライブラリがWebに対応しているかを念入りに確認するべきです。具体的にはReact Native Directoryのfilter機能によって調べたり、ライブラリのページから使用可能かを調べた上で判断することになると思われます。