結論から言うと、はい。Appleは一定の制限内でLive Updateを許可しています。 アプリの性質自体が変わらない限り、App Storeの再審査なしでiOSアプリに新しいJavaScript、HTML、CSSを配信できます。ただし、ネイティブのcodeをダウンロードすることはできません。
混乱の多くは、解釈されるcodeのLive Update(許可)と、新しい実行可能な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やアプリケーションのためのストアまたはストアフロントを作成しないこと、(c) OSの署名、サンドボックス、その他のセキュリティ機能をバイパスしないこと。
つまり、ルールは「ダウンロードされた code は禁止」ということではありません。「ダウンロードされた ネイティブ 「codeのダウンロードは禁止」ではなく、「codeのダウンロードは、3つの条件を満たすcodeに限る」というのが正しい解釈です。
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 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 | Native code は不可、WebKit/JavaScriptCore 上の JS は可 | リモート構成およびサーバー駆動 UI |
| .NET MAUI / Xamarin | AOT コンパイルされた C# マシン code | いいえ | リモート構成のみ |
| Kotlin / Compose Multiplatform | Kotlin/Native コンパイルされたマシン code | いいえ | リモート構成のみ |
| 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(Over-the-Air)で更新可能です。これはExpo UpdatesやMicrosoft CodePush(2025年に終了)が実現しようとしていた仕組みです。 CodePushから移行するチームは 代替手段を必要としています。
ネイティブモジュールやReact Nativeのバージョンを変更する場合、 Info.plist 新しいバイナリの作成が必要です。
Flutter:インタプリタ経由のみ可能
iOSでは、FlutterはDartを事前にARMマシン用 code にコンパイルします。新しいマシン用 code のダウンロードは禁止されているため、標準のFlutterではLive Updateを利用できません。Shorebirdは、パッチを当てたDart code をインタプリタで実行することでこれを解決し、インタプリタによる code の例外規定に適合させています。詳細は Flutterアプリ向けのLive Updateについて.
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
最初のアップデートを送信するには、 クイックスタート に従ってください。