Expo Updates でチャンネルを切り替える:実行時に更新チャンネルを切り替える方法
Expo の OTA Update Channel Surfing を使えば、変更を特定のユーザーに即座に配信できる。再インストールなしで、実行中に更新チャンネルを切り替えられるようになった。
日本語
コピー

こんな経験はないだろうか。特定のユーザーに変更を直接届けて、すぐ試してもらい、フィードバックをもらいたい。ただし TestFlight から新しいビルドをダウンロードしてインストールしてもらう手間はかけさせたくない。自分にも覚えがある。
すぐ実装できる改善を顧客が提案してくることはある。だが、その結果を手元で確認してもらうには、全ユーザーにアップデートを配信するか(実験的な変更としてはリスクが大きく、帯域も無駄になる)、その顧客専用にビルドを用意するか(双方にとって面倒だ)しかなかった。
足りなかったのは柔軟性だ。非技術系のステークホルダー、QA、あるいは適切なタイミングで全ユーザーなど、相手に応じて異なるアップデートをオンデマンドで届けたい。開発者はそう考えていた。
これまで、プロダクションビルドから開発中のバージョンに切り替えてフィードバックを集め、またプロダクションビルドに戻す手段は存在しなかった。
「channel surfing」はまさにこの問題を解決する。デバイスにインストール済みのアプリが実行時に更新チャネルを切り替えられるようにすることで、プロダクションアプリは固定された終着点ではなく、いつでもレビューと反復ができる場になる。これはプロダクションアプリを使う非技術系のステークホルダーに特に有効で、すでにインストールされているアプリの中で変更を試し、フィードバックを返せる。
更新チャネルとは
更新チャネルは、EAS Update が更新を特定のビルドに届ける仕組みだ。各ビルドはチャネルに関連付けられ、どの更新を受け取るかはそのチャネルが決める。
たとえば preview チャネルに更新を公開しても、production のユーザーには影響しない。これまでチャネルを切り替えるには、別のネイティブビルドをインストールする必要があった。
更新チャネルに馴染みがなければ、EAS Update のドキュメントで詳しく説明している。
channel surfing とは
channel surfing を使うと、インストール済みのアプリが再インストールなしで別の更新ストリームから更新を取得できる。インストール済みのアプリは実行時に更新チャネルを切り替えられ、その後はアンインストールされるか別のチャネルに切り替えるまで、新しく選んだチャネルから更新を受け取り続ける。
実際の使い方はこうだ。プロダクトオーナーや QA は production ビルドをたとえば preview チャネルに切り替え、最新の変更を試せる。確認が終わったら production に戻す。再インストールも、プレビュー用のビルドを別途用意する必要もない。
仕組みとしては、channel surfing はアプリが updates クライアントにどのチャネルを使うかを伝えるものだ。この選択は実行時に変更でき、クリアまたは上書きされるまで有効だ。
channel surfing の実装方法
channel surfing を試すには、プロジェクトで EAS Update を設定しておく必要がある。設定方法は EAS Update のはじめかたを参照してほしい。
本質的に、channel surfing は API 呼び出し1つで動く。
Updates.setUpdateRequestHeadersOverride({
"expo-channel-name": "your-channel",
});
これは EAS Update を問い合わせるときに使う channel header を設定する。詳しくは setUpdateRequestHeadersOverride API を参照。
より快適に使うなら、チャネルを切り替えて次のアプリ再起動を待つだけでは通常不十分だ。よくあるのは、すぐに更新を確認し、あればダウンロードしてアプリをリロードし、ユーザーを選択したチャネルの更新バージョンへ直接導く方法だ。
典型的な流れは次のとおり。
チャネルを切り替える(setUpdateRequestHeadersOverride)
更新を確認する(checkForUpdateAsync)
更新を取得して適用する(fetchUpdateAsync)
アプリをリロードする(reloadAsync)
これらをつなぐ簡単な例はおおよそ次のようになる。
import * as Updates from "expo-updates";
async function channelSurfAsync(selectedChannel: string) {
// Set the updates channel
Updates.setUpdateRequestHeadersOverride({
"expo-channel-name": selectedChannel,
});
// Check if an update is available
const { isAvailable } = await Updates.checkForUpdateAsync();
if (isAvailable) {
// Fetch and install the update
await Updates.fetchUpdateAsync();
}
// Reload the app
await Updates.reloadAsync({
reloadScreenOptions: {
backgroundColor: "#F8FAFC",
spinner: {
enabled: true,
size: "large",
},
fade: true,
},
});
}
この流れをどう組み立てるかは自由だ。複数の操作に分けてもいいし、一度にすべて実行してもいい。どちらにせよ、失敗する場合を考慮しておくこと。ネットワークの問題や無効なチャネルなどで、更新が適用されないことがある。
channel surfing は通常、アプリの全ユーザーではなく一部のユーザーだけに開放する。たとえばログイン済みの社員にだけボタンを表示し、押すとアプリを preview チャネルに切り替える、といったことができる。利用可能なチャネルの一覧はアプリ内で定義する必要がある。既存の更新チャネルを取得する公開 HTTP API は現時点で存在しない(ロードマップにはある)ので、チャネル切り替えの UI は既知のチャネル名の集合を元にするか、eas channel:list を呼び出すエンドポイントを自分で用意することになる。
channel override を設定すると、そのデバイス上の以降の更新関連の操作では、アプリがアンインストールされるか override 自体が上書きされるまで、EAS Updates API がこの channel を使う。
ちなみに、SDK 54 の reloadAsync ではリロード時の体験をより細かく制御できる。アプリのリロード中に表示する UI をカスタマイズでき、以前のように一瞬空白が表示される問題を避けられる。アプリの更新がより整然と、滑らかに見えるようになる。
channel surfing が機能するかテストする方法
channel surfing の効果を実際に確認するには release build が必要だ。expo-updates API の機能の多くはrelease build でのみ利用できる(ビルド時に EX_UPDATES_NATIVE_DEBUG を有効にしている場合を除く)。debug build では、通常アプリは開発サーバーから JavaScript を読み込むため、通常の更新フローが迂回される。
テストしやすいように、EAS Build で release build を作りましょう。EAS Build をまだ設定していない場合は、EAS Build 入門ガイドを参照してください。
EAS Build では、development build を明示的に選ぶ(たとえば developmentClient: true を使う)か、ネイティブのビルド設定を明示的に上書きしない限り、ビルドはデフォルトで release 設定を使います。この profile では release 設定はそのままにして、出力形式だけを変えます。iOS シミュレータ用アプリ(ios.simulator: true)と、そのままインストールできる Android APK(android.buildType: "apk")です。ローカルでのテストに便利です。
{
"cli": {
"version": ">= 16.28.0",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"channel": "development"
},
"preview": {
"distribution": "internal",
"channel": "preview"
},
"production": {
"autoIncrement": true,
"channel": "production"
},
+ "production-simulator": {
+ "channel": "production",
+ "ios": {
+ "simulator": true
+ },
+ "android": {
+ "buildType": "apk"
+ }
+ }
},
"submit": {
"production": {}
}
}
あとはプロジェクトをビルドするだけです。
eas build --profile production-simulator --platform [ios/android]
ビルドが完了したら、ダウンロードしてシミュレータか実機にインストールします。
更新を公開して channel を切り替える
アプリをインストールしたら、別の channel に更新を公開します。
eas update --channel preview
次にアプリ内で channel surfing UI を開き、channel の切り替えをトリガーします。たとえば、production と preview の 2 つの channel を行き来するボタンをいくつか用意しておきます。切り替えが実行されると、アプリは選択した channel から更新を取得し、新しい更新にリロードされるはずです。
Expo OTA 更新の落とし穴
以下はどれも channel surfing 固有の問題ではありませんが、実行時に channel を切り替え始めるとすぐに表面化します。
ランタイムバージョンの不一致
更新のランタイムバージョンがインストール済みアプリのランタイムバージョンと一致しない場合、その更新はダウンロードも適用もされません。channel surfing では、channel は切り替わったのに更新がまったく適用されない、という形で現れます。その channel に更新が確かに存在していてもです。
多くの場合、その更新が別のネイティブバージョンのアプリから公開されたものだという意味になります。
更新の削除とロールバック
もうひとつ見落とされがちなのが、更新がどう削除・ロールバックされるかです。アプリがある channel の更新をすでにダウンロードしている場合、EAS dashboard からその更新を削除しても(あるいは eas update:delete を使っても)、その更新を持っているデバイスからは消えません。削除が止めるのは今後のダウンロードだけです。
壊れた更新をロールバックする最も確実な方法は、既知の正常な更新を再公開することです。同じ channel に再公開すると、その channel の履歴の先頭に新しい更新が作られ、クライアントはそれを最新版として適用します。
別の方法として、EAS Update にはロールバックの仕組み(eas update:rollback)があり、クライアントに以前の安定した更新を再適用させたり、ビルドに埋め込まれた更新まで戻したりできます。
channel surfing がモバイルのイテレーションを改善する理由
本番環境で変更を素早くレビューする必要があるとき、channel surfing は特に役立ちます。
たとえば、広く配信する前に検証したい緊急のバグ修正があるとします。channel surfing を使えば、その変更を少数の指定ユーザーだけに隔離し、本番に入る前にレビューしてもらえます。
プロダクトオーナーや QA は、インストール済みの本番ビルドを別の更新 channel に切り替え、修正や機能を検証し、終わったら元に戻せます。
これにより、技術に詳しくない関係者もレビューや意思決定に参加しやすくなり、ワークフローも止まりません。1 つの本番ビルドが、テスト・フィードバック・検証のための柔軟なツールになるわけです。
channel 切り替えのリスクと注意点
channel を切り替えると、アプリが実行する JavaScript bundle が変わります。channel 間で互換性のないマイグレーションやデータ形状に依存していると、行き来するうちに問題が起きることがあります。
たとえば、beta の更新がデータベースマイグレーションを実行した場合、本番バージョンは新しい schema を理解できないかもしれません。切り替えても安全なように更新を設計するか、必要なら一方向の切り替えだけに制限するべきです。