OTA Updateとは
OTA の意味
OTA(Over The Air)Update とは、アプリを再インストールせずにネットワーク経由で更新を配信する仕組みです
Webでいうと:
1. JS / CSS を差し替える
2. ページをリロードする
に似ていますが、ネイティブアプリでは仕組みが異なります
ネイティブアプリでいう OTA Update
ネイティブアプリ(iOS / Android)では通常
1. App Store / Google Play に提出
2. 審査
3. ユーザーが更新
というフローが必要です
OTA Updateを使うと:
1. JavaScript(ロジック・UI)だけ更新
2. サーバーから直接配信
3. ユーザーは再インストール不要
という更新が可能です
Expo ではこれを EAS Update(expo-updates) として提供しています
どこまで OTA Update をやっていいのか?
ここが一番重要なポイントです
結論からいうと:
→ 「ネイティブ挙動を変えない範囲の JS 更新」まで
App Store(Apple)の考え方
Apple の App Store Review Guidelines には、
OTA Update を明示的に禁止する条項はありません
ただし、公式に「許可する」という声明もなく実務上は、アプリの実質的な機能変更ではないかどうかの判断が審査チームに委ねられています
Apple が気にするポイント
- アプリの主目的や主要機能が変わっていないか
- ストア申請時の説明と挙動が乖離していないか
- ユーザーにとって誤解を招く変更になっていないか
そのため、一般的には問題ないと判断されやすい例:
- バグ修正
- UI文言の変更
- 表示や軽微なUX改善
注意が必要な例:
- メイン機能の追加・削除
- 課金フローや決済仕様の変更
- アプリの性質が変わるレベルの更新
という線引きで考えられることが多いです
Google Play の考え方
Google Play では、
実行可能なネイティブコードを Google Play 以外から更新することは原則禁止されています
ただし:
- JavaScript
- HTML
- インタープリタ上で動作するコード
については 例外として扱われるケースがある とされています
これは React Native / Expo OTA が一般的に許容されている理由でもあります
実務上は以下の点が重要です
-
.dex/.soなどのネイティブコード → OTA 不可 - JS / アセット → OTA 可能(ただし責任は開発者側)
OTA Update を試してみる
今回やった構成
- Expo(EAS)
- Android アプリ
- EAS Build + EAS Update
- DeployGate で配布
iOSアプリだとApple Developerに登録が必要になるため今回は誰でも試せる構成にしています
実際にやった手順(概要)
eas build --platform android --profile production- アプリを DeployGate にアップロード
- JS を修正
eas update --branch production -m "ota works finally"
この流れで preview update では確実に反映される状態になりました
詰まったポイント・ハマりどころ
OTA Updateされない原因
{
"expo": {
...
"owner": "roll1226",
"runtimeVersion": "1"
}
}
この設定の場合:
- runtimeVersion が一致する限り
- すべて同じ OTA Update の対象 になります
結果として:
- update group の commit ID がすべて同じに見える
- 何が更新されているのか分かりにくい
という状態になりました
実務的におすすめ:
{
"expo": {
...
"owner": "roll1226",
"runtimeVersion": {
"policy": "appVersion"
}
}
}
- ネイティブコードが変わったら runtimeVersion を変える
- JS だけなら OTA で吸収する
という運用がしやすくなります
自動更新を止め方
{
"expo": {
...
"updates": {
"enabled": true,
"checkAutomatically": "ON_LOAD",
"fallbackToCacheTimeout": 0,
"url": "https://u.expo.dev/xxxxxxxxxxxxxxxxx"
},
...
}
}
この設定だと:
- アプリ起動時に自動で更新を確認
- 条件次第で即反映される
だったので
{
"expo": {
...
"updates": {
"enabled": true,
"checkAutomatically": "NEVER",
"fallbackToCacheTimeout": 0,
"url": "https://u.expo.dev/xxxxxxxxxxxxxxxxx"
},
...
}
}
- アプリ起動時に自動で更新しないように設定
- 手動更新 UI を出したいのに勝手に更新される
という対応にしました
手動更新 UI を出す実装
考え方はシンプルで:
- 起動時に
checkForUpdateAsync() - 更新があればフラグを立てる
- ボタンで
fetchUpdateAsync()+reloadAsync()
という流れです
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
setUpdateAvailable(true);
}
この実装をすることで
- 自動更新はしない
- アプリを開いている間に更新が来たら通知する
という挙動を実現できます
アプリ起動中に OTA が来たら通知
- 定期的に
checkForUpdateAsync()を呼ぶ - フォアグラウンド復帰時にチェックする
以下のようなアップデート通知 UI を表示することができます
まとめ
- Expo OTA Update は 実務でも十分使える
- ただし「何でも OTA」は危険
- runtimeVersion の設計がかなり重要
- 自動更新はオフ + 手動 UI が安心
OTA Update は強力ですが、ストア審査・ユーザー体験・運用ルールを意識して使うことが大切だと感じました
今回記事を書く時に書いたコードは下記になります