結論から言うと、Appleは制限の範囲内でライブアップデートを認めています。 アプリの性質自体が変わらない限り、App Storeの審査を通さずに新しいJavaScript、HTML、CSSをiOSアプリに配信できます。ただし、ネイティブのcodeをダウンロードすることはできません。
混乱の多くは、インタプリタで実行されるcodeのライブアップデート(許可)と、新しい実行可能なcodeのダウンロード(禁止)という、2つの異なる概念の混同に起因します。このページでは規約を引用し、その境界線がどこにあるのか、Appleがそれをどう執行しているのかを解説します。
これは法的助言ではありません。現時点で規約がどう読めるか、そして数千ものアプリが本番環境でそれをどう使っているかを示したものです。
Appleの規約の真実
重要なのは2つの条文です。1つ目は、 App Store Review Guideline 2.5.2:
アプリは自身のbundle内で完結している必要があり、指定されたコンテナ領域外のデータの読み書き、および他のアプリを含むアプリの機能や動作を変更・追加するcodeのダウンロード、インストール、実行は禁止されています。
これだけを読むと禁止されているように聞こえますが、例外はもう一つの条文、つまりすべての開発者が署名する Apple Developer Program ライセンス契約の第3.3.1(B)項に記載されています:
解釈可能なcodeは、以下のcodeを満たす場合に限り、アプリケーションにダウンロードできます。(a) App Storeに提出した際の意図や広告内容と矛盾する機能を提供することで、アプリケーションの主な目的を変更しないこと、(b) 他のcodeやアプリケーションのためのストアまたは storefront を作成しないこと、(c) OSの署名、sandbox、またはその他のセキュリティ機能をバイパスしないこと。
つまり、ルールは「ダウンロードされた code は禁止」ということではありません。「ダウンロードされたものは禁止」なのです。 ネイティブ 「code、およびcodeの解釈は、以下の3つの条件を満たす場合に限られます」
CapacitorアプリのWebレイヤーは、JavaScript、HTML、CSSで構成され、 WKWebViewこれはApple独自のWebKitエンジンです。codeとして解釈されるため、例外規定に該当します。ガイドライン4.7でも同様に、バイナリに埋め込まれていないHTML5やJavaScriptコンテンツの実行は、Appleによって明示的に許可されています。
境界線はどこにあるか
| live updateなら許可 | live updateは許可されない |
|---|---|
| JavaScript、HTML、CSSのバグ修正 | Swift、Objective-C、またはコンパイル済みバイナリのダウンロード |
| 既存のWebスタックにおけるUI変更、テキスト変更、新規画面の追加 | アプリを別の製品へ変更すること(メモアプリをカジノアプリにするなど) |
| Web codeのA/Bテストや段階的なロールアウト | App Review時に隠していた機能のアンロック |
| 画像、翻訳、設定の更新 | 他のアプリや code をインストールするストアの構築 |
| 不具合のあるリリースのロールバック | iOSのサンドボックス、署名、権限の回避 |
Capgo は左側のカラムのみを配信します。これはアプリ内のウェブバンドルを置き換えるものです。新しい Capacitor プラグイン、新しい権限、または SDK のアップグレードなど、ネイティブに関する変更は、引き続き通常の App Store リリースが必要です。Capgo の バージョン・ターゲティング は、新しいネイティブ code を必要とするバンドルが、古いバイナリに配信されないようにします。
Appleによる制限の仕組み
Appleが判断するのは技術ではなく、その実態です。タイポの修正を OTA で行うことで問題になることはありません。Appleが問題視するのは、審査時はある状態だったアプリが、その後別のものに変貌してしまうパターンです。これはガイドライン 2.5.2 だけでなく、2.3.1(隠れた機能や未記載の機能の禁止)にも該当します。
実践的なアプローチ:
- 本来提出するはずだったものを、より速く配信するだけです。
- すべてのアップデートを、App Store の説明文に記載された目的の範囲内に収めてください。
- 大規模な新機能のリリースは App Review を通じて行い、リリース間の修正や改善には Live Update を活用しましょう。
- 審査員が確認していない挙動を有効にするために、live update を決して使用しないでください。
そのルールに従えば、Live UpdateはiOSアプリのリリースで当たり前に使われるものです。React NativeのチームはCodePushやExpo Updatesで何年も前から行っていますし、CapacitorのチームはCapgoで行っています。
iOSでそれができるスタックはどれですか?
iOSアプリでOTAアップデートを配信できるかは、1つの問いにかかっています。アプリのロジックは実行時に解釈されるか? JavaScriptなどのインタープリタ型codeなら可能です。Swift、Objective-C、C++のようにマシンcodeにコンパイルされるものは不可です。FlutterのようなAOTコンパイル型フレームワークは、インタープリタ経由でのみ可能です。
スタック別のiOS live update互換性
| スタック | iOSでcodeが動作する仕組み | ライブアップデートは可能? | ツール |
|---|---|---|---|
| Capacitor | WKWebView (WebKit) 上の JavaScript, HTML, CSS | はい | Capgo |
| Ionic (Capacitor で) | Capacitor と同様 | はい | Capgo |
| Cordova | WKWebView 上の JavaScript | はい | Capacitor への移行後の Capgo, レガシーな Cordova プラグイン |
| React Native / Expo | Hermes または JavaScriptCore 上の JavaScript bundle | はい、JavaScriptのみ | Expo Updates、自前運用のCodePush代替案 |
| NativeScript | 組み込みエンジン上のJavaScript | はい、JavaScriptのみ | 限定的、主に自作によるもの |
| Flutter | DartをマシンコードにAOTコンパイルしたcode | インタプリタ経由のみ | Shorebird (パッチ用Dartインタプリタ) |
| ネイティブのSwift / Objective-C | マシンcode | ネイティブ code は不可、WebKit/JavaScriptCore 上の JS は可 | リモートコンフィグとサーバー駆動 UI |
| .NET MAUI / Xamarin | マシン code に AOT コンパイルされた C# | いいえ | リモートコンフィグのみ |
| Kotlin / Compose Multiplatform | マシン code にコンパイルされた Kotlin/Native | いいえ | リモートコンフィグのみ |
| Unity / Unreal | IL2CPP または C++ でコンパイルされた C# | code はNG、コンテンツならOK | アセットバンドル、スクリプトコンテンツ (Lua) |
| Webラッパー (リモートURL) | WebViewで読み込まれるウェブサイト | 常にライブ更新可能だが、審査リスクあり | 不要、下記の注釈を参照 |
Capacitor、Ionic、Cordova:完全にカバー
アプリのWeb UIとJavaScriptのビジネスロジックは、 WKWebView次で実行されます。ネイティブシェルとプラグインはネイティブのままです。Apple Developer Program License Agreementのセクション3.3.1(B)では、ダウンロードされた解釈可能な code は、アプリの主な目的を維持し、code のようなストアを作成せず、OSのセキュリティを回避しない場合にのみ許可されています。ガイドライン4.7では、HTML5やJavaScriptのミニアプリを提供するアプリを別途規定しており、追加の要件があります。
live update はWebバンドルを置き換えます。ネイティブシェル、プラグイン、権限はAppleが審査したものと同じです。そのため、Capgo は設計上コンプライアンスを遵守しています。たとえ意図したとしても、ネイティブの code を変更することはできません。新しいネイティブプラグインや権限の追加には、引き続きApp Storeでのリリースが必要です。
React NativeとExpo:JavaScriptはOK、ネイティブモジュールはNG
React Nativeは、HermesまたはJavaScriptCore上でJavaScriptバンドルを実行します。そのバンドルは解釈可能な code であり、OTAで更新可能です。これはExpo UpdatesやMicrosoft CodePush(2025年に終了)が実現しようとしていたことです。 CodePushから移行するチームが 代替手段を求めています。
ネイティブモジュールやReact Nativeのバージョンを変更する場合、 Info.plist 新しいバイナリが必要です。
Flutter: インタープリタ経由のみ可能
iOSでは、FlutterはDartを事前にARMマシン用 code にコンパイルします。新しいマシン用 code のダウンロードは禁止されているため、標準のFlutterではlive updatesを利用できません。Shorebirdは、パッチを当てたDart code をインタープリタで実行することでこれを解決し、解釈可能な code の例外規定に適合させています。詳細は Flutterアプリ向けのlive updates.
Native Swift, .NET MAUI, Kotlin Multiplatform: ネイティブ code 更新は不可
これらのスタックはネイティブなマシン用 code を生成します。そのため、代替となるネイティブ code をダウンロードすることはできません。ただし、Appleの条件を満たしていれば、JavaScriptCore経由のJavaScriptのような、解釈可能な code をダウンロードして実行することは可能です。また、OTAで変更可能なものとして、リモート設定、レビュー済みの機能のフィーチャーフラグ、サーバー駆動UI、画像、テキストなどがあります。
Unityおよびゲームエンジン: コンテンツはOK、code はNG
ゲームの code はコンパイルされています。アセットバンドル(レベル、アート、バランス調整データ)はコンテンツであり、ダウンロード可能です。Luaのような組み込みスクリプト言語は解釈可能な code であり、JavaScriptと同じルールに従います。
リモートURLラッパー: 実現可能だが、審査のリスクあり
WebViewでウェブサイトを表示するだけのアプリは、サイトをデプロイするたびに「更新」されていることになります。問題はガイドライン2.5.2ではなく、4.2(最小限の機能性)です。Appleは単にウェブサイトをフレーム内に表示しているだけのアプリをリジェクトします。Capacitorでウェブアプリをバンドルし、ネイティブ機能を追加してLive Updateを活用することで、この問題を回避できます。
iOSにおいて、どのスタックでも不可能なこと
どのフレームワークを使用していても、live updateでは以下ができません:
- ネイティブのcode、フレームワーク、または動的ライブラリのダウンロード
- 新しい権限(Permissions)やEntitlementsの追加
- App Storeの掲載内容と異なるアプリへの変更
- App Review時に隠していた機能の有効化
次のステップ
- Androidの場合: GoogleはPlay Storeの審査なしでLive Updateを許可していますか?
- 詳細な運用ルール: AppleによるOTAアップデート制限の適用方法
- 承認ワークフロー: Capacitor OTAアップデート: App Store承認ガイド
- 実際のリジェクト事例: App Storeリジェクトの恐怖体験談
レビュー待ちではなく、今日中に修正をリリース
Capgo Live Updates チャネル、ロールバック、差分アップデート、エンドツーエンド暗号化を備え、JavaScript、HTML、CSSの変更を数分でCapacitorアプリにpushできます。オープンソースでセルフホストも可能です。
bunx @capgo/cli@latest init
最初のアップデートを送信するには、 クイックスタート に従ってください。