📚

Expo の仕組みと使い方をメモしていく記事

2021/12/22に公開

これは iOS アプリを開発しながら Expo について調べたことを自分用にまとめておく記事です。

Expo とは

Expo はクロスプラットフォーム(iOS/Android/Web)で React Native アプリを開発するための便利機能を集めたプラットフォーム。

素の React Native プロジェクトと比較して若干の制約があるが、 Expo SDK が提供する様々な機能を使えたり開発環境の整備が簡単というメリットがある。Expo は Expo SDK, Expo CLI, Expo Go, EAS と呼ばれる4種類のツールで構成されている。

Expo SDK

Expo でアプリを作るために必要なあれこれを集めたパッケージ。

開発に使われているリポジトリは主に以下のディレクトリで構成されている。

  • packages: Expo の API を提供するためのソースコード(ネイティブコードを含む)で、例えば expo-camera など
  • apps: Expo 自体の開発時にテストするために使うサンプルアプリ
  • docs: expo.dev の公式ドキュメント
  • templates: expo start 時に選択するアプリの雛形で、現在は4種類
  • react-native-lab: React Native の Fork
  • guides: Expo JavaScript Style Guide のような Expo 自体の開発におけるガイド
  • android: Expo アプリの Android プロジェクト
  • home: Expo アプリの JavaScript コード
  • ios: Expo アプリの iOS プロジェクト
  • tools: Expo 自体の開発時に利用されるライブラリ

React Native と Expo

Expo は React Native を Fork し、そのアップデートに追従しながら SDK に組み込んでいる。

2021年12月21日の時点で Expo SDK の最新 ver は v44 で、対象となる React Native の ver は 0.64.3 である。しかし、 React Native の最新 ver は 0.66.4 なので、最新版からは少し遅れている。

これは Expo が提供する JS-Native ブリッジやデバッグツール、各種コンポーネント、バンドラーや開発サーバーなどが React Native と連携しており、その互換性を保つ開発が必要なため。

Expo SDK は3ヶ月に一度(3, 6, 9, 12月)の定期リリースを行なっているが、 React Native のリリースは不定期であり且つ後からバグが見つかって patch リリースされることもある。Expo の開発チームは SDK の次 ver 開発時にその時点で安定的に利用できると確信できる React Native の ver を評価しているようだ。

https://github.com/expo/fyi/blob/main/react-native-releases.md

このように最新の React Native をすぐに使えないという点は Expo を使うトレードオフの一つ(ちなみに Bare Workflow であれば使える)。もっとも、最新の React Native を使う場合は Expo の開発者がやっている作業を自分でやる必要があるので特にデメリットとは感じないかもしれない。

Expo CLI

Expo で開発するときに便利なツールを集めた CLI。例えば以下の機能がある。

  • init: アプリの雛型を生成する
  • install: Expo SDK と互換性のある npm パッケージをインストールする
  • start: 開発サーバーを起動する
  • build: iOS/Android/Web 向けにアプリをビルドする
  • publish: expo.dev のサーバーにアプリをアップロードする。OTA アップデートもできる(参考: ExpoのOTA updateの仕様について

詳しくはドキュメントを参照。

https://docs.expo.dev/workflow/expo-cli/

開発サーバー

expo start で開発サーバーを起動した時に何が起きているのか。

Expo CLI は @expo/ngrok で新たな tunnel process を生み出しており、開発機の LAN 外から開発機にアクセスできる。そのため、ファイアウォール等の設定は必要ない。

開発サーバーを起動すると、 app.json を基に Manifest が生成される。Expo Go はまず Manifest を取得し、そこで指定した bundleUrl からアプリの JavaScript を取得する。

React Native では iOS/Android における JavaScript の実行環境として JavaScriptCore を使うがこれでは JSX は動かないので、 Babel を通じてJavaScriptCore が解釈できる JavaScript にトランスパイルされる。さらに @expo/metro-config の設定を使い Metro で iOS/Android 向けに JavaScript をバンドルする。Web 版では Webpack を使う。

Hermes

最近の React Native はアーキテクチャが大きく変わっており JavaScript の実行エンジンとして Hermes を使うこともできる。起動速度が速くなり、メモリ使用量やアプリサイズの削減など様々なメリットがある。

Expo では app.json で jsEngine を指定することで Hermes を使うことができる。前提として、アプリのビルド時に後述の EAS Build が必須となる。詳しくは公式の Using Hermes Engine を参照。

Expo Go iOS / Android

Expo でアプリを開発するためのクライアント。いわゆるシミュレーターと異なり、手元のスマートフォン上で動く。

CLI から expo start で開発機でサーバーを起動し、コンソールまたはブラウザ上で表示された QR コードを読み込むことで手元の端末からアプリの動作確認ができる。また、 expo publish で expo.dev 上にアップロードしたアプリをダウンロードすることもできる(参考: Sharing pre-release versions of your app)。

Expo Go は開いた全てのプロジェクトの最新版を自動的にキャッシュする。他にもスマートフォンを振るとデバッグメニューが出てくるなどの機能が地味に便利。

なお Expo Go には Expo SDK に含まれていないネイティブコードを呼び出すことはできないという制約がある。これを受け入れられない場合は、Xcode や Android Studio で iOS や Android のシミュレーターを使うことになる。

https://docs.expo.dev/workflow/ios-simulator/
https://docs.expo.dev/workflow/android-studio-emulator/

ただし、本記事公開時点では preview だが、 Development Builds という custom development client を使えば Expo SDK 以外のネイティブコードに依存していても Expo Go のような開発体験を得られる。今後に期待。

Expo Application Services(EAS)

EAS Build と EAS Submit の2種類のサービスがある。基本無料だが、お金を払うとアプリのビルドを並列化できたり無料ユーザーより先にビルドしてくれたりする。

https://expo.dev/pricing

商用利用の場合は、毎月 $1,500 超の売上を稼いでいるプロジェクトは無料プランを使うことはできない(EAS Fair Use Policy を参照)。

普通は Expo アプリを build または submit するために使うと考えられるが、 npx create-react-native-appnpx react-nativeignite-cli などで作った(非 Expo の)アプリでも使うことができる。

全体像の把握にはリリースの記事と動画が参考になる。

EAS Build

アプリのバイナリをビルドすることができる。CI 環境からビルドすることもできる。

開発機で expo build [ios|android] するのと比較して、次のようなメリットがある。

  • Expo のサーバー上でアプリをビルドし、 App Store や Google Play へ提出するための署名をやってくれる(参考: App credentials explained
  • 簡単にテスト用のアプリを配布できる(参考: Internal distribution
  • アプリのサイズを削減できる

EAS Build によるビルドでは、 Expo SDK 全体ではなくプロジェクトの package.json に追加されているパッケージのみを含めるようにビルドされるため、アプリのサイズが大きく削減される。

このトレードオフとして OTA アップデートでアプリの JavaScript を差し替えようとするとクラッシュする可能性がある。例えば、端末に配布される JavaScript バンドルが存在を期待する Expo SDK のパッケージがランタイムに無い、という場合が考えられる。公式の Binary Compatibility and runtime vesions を参照。

この問題を解決するため、 Expo は EAS Update という機能を開発しており、本記事公開時点では preview で提供している。詳細な仕組みについては公式の How EAS Update works が参考になる。

EAS Build の iOS では Fastlane が使われている。

EAS Submit

Expo が運営するサーバーから App Store と Google Play Store にアプリをアップロードすることができる。CI 環境から Submit することもできる。

ストア提出に必要な設定は eas.json に記述する。

原則的には EAS Build で作られたアプリを Submit するためのサービスだが、開発機で expo build:[ios|android] してビルドしたアプリをアップロードすることもできる(参考: Using EAS Submit with "expo build")。

Expo を使った開発の流れ

基本的には

  1. CLI で開発サーバーを起動し
  2. SDK の機能を使ってアプリを書き
  3. Expo Go で動作確認する

という流れを繰り返してアプリを開発する。公式ドキュメントの Getting Started とチュートリアルで一通り体験できる。アプリができたら、 EAS Build で実行ファイルを作って EAS Submit でストアにアップロードする。

Expo では基本的に最初は Managed workflow(チュートリアルで紹介されているやり方)で開発し、必要に応じて eject して Bare workflow に移行する。Managed では原則的に Expo が対応していないカスタムのネイティブコードを追加できないため、サードパーティの SDK を追加する場合は eject する必要がある。

最初から Bare で開発しても問題はないが、 Expo Go の利用に制約があるなど不便なことが多く、且つ eject するのはコマンド一つでできるので最初は Managed で始めるのが良さそう。

https://docs.expo.dev/introduction/managed-vs-bare/

Expo の Managed workflow を通じた開発について詳しくは以下の記事が参考になる。

Expo SDK で使う機能

https://docs.expo.dev/versions/v44.0.0

気が向いたら追記する。

おまけ: 最近の React Native

React Native は最近アーキテクチャが大きく変わりつつあり、(JavaScript の実行エンジンだけでなく)アプリの起動や動作の速度、メモリ使用量などのパフォーマンスを大きく改善するようなアップデートが多い。サードパーティのツールも徐々に対応していくと考えられるのでこれからが楽しみ。

Discussion