Short answer: yes, Apple allows live updates, within limits. You can push new JavaScript, HTML and CSS to an iOS app without a new App Store review, as long as the update does not change what the app is. You cannot download native code.
With Capgo: Capgo live updates ship only the web layer (JavaScript, HTML, CSS, assets) of a Capacitor app, which Apple’s rules allow when the update keeps the app’s primary purpose, does not create a code storefront, and does not bypass OS security; native changes still go through a store release.
Most of the confusion comes from mixing two different things: live updates of interpreted code (allowed) and downloading new executable code (not allowed). This page quotes the rules, shows where the line is, and explains how Apple enforces it.
This is not legal advice. It is how the rules read today and how thousands of apps use them in production.
What Apple’s rules actually say
Two texts matter. The first is App Store Review Guideline 2.5.2:
Apps should be self-contained in their bundles, and may not read or write data outside the designated container area, nor may they download, install, or execute code which introduces or changes features or functionality of the app, including other apps.
Read alone, that sounds like a ban. The exception lives in the second text, section 3.3.1(B) of the Apple Developer Program License Agreement, which every developer signs:
Interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS.
So the rule is not “no downloaded code”. It is “no downloaded native code, and interpreted code only under three conditions”.
A Capacitor app’s web layer is JavaScript, HTML and CSS running in WKWebView, which is Apple’s own WebKit engine. That is interpreted code, so it falls under the exception. Guideline 4.7 points the same way: Apple explicitly lets apps run HTML5 and JavaScript content that is not embedded in the binary.
Where the line is
| Allowed with a live update | Not allowed with a live update |
|---|---|
| Bug fixes in your JavaScript, HTML, CSS | Downloading Swift, Objective-C or any compiled binary |
| UI changes, copy changes, new screens in your existing web stack | Changing the app into a different product (a notes app becoming a casino) |
| A/B tests and gradual rollouts of web code | Unlocking features hidden from App Review |
| Updating images, translations, config | Building a store that installs other apps or code |
| Rolling back a bad release | Bypassing the iOS sandbox, signing or permissions |
Capgo only ships the left column. It replaces the web bundle inside your app. Anything native, such as a new Capacitor plugin, a new permission, or an SDK upgrade, still needs a regular App Store release. Capgo’s version targeting makes sure a bundle that needs new native code never reaches an old binary.
How Apple enforces it
Apple judges substance, not technology. Nobody gets pulled for fixing a typo over the air. The pattern Apple goes after is an app that passes review as one thing and becomes another thing afterwards. That is guideline 2.3.1 (no hidden or undocumented features) as much as 2.5.2.
In practice:
- Ship what you would have submitted, just faster.
- Keep every update within the purpose described in your App Store listing.
- Send big feature launches through App Review, and use live updates for fixes and iteration between releases.
- Never use a live update to switch on behavior that reviewers did not see.
If you follow that, live updates are a normal part of shipping an iOS app. React Native teams have done it for years with CodePush and Expo Updates, and Capacitor teams do it with Capgo.
Which stacks can do it on iOS?
Whether your app can ship over-the-air updates on iOS depends on one question: is your app logic interpreted at runtime? JavaScript and other interpreted code, yes. Swift, Objective-C and C++ compiled to machine code, no. AOT-compiled frameworks such as Flutter qualify only through an interpreter.
iOS live update compatibility by stack
| Stack | How the code runs on iOS | Live updates allowed? | Tooling |
|---|---|---|---|
| Capacitor | JavaScript, HTML, CSS in WKWebView (WebKit) | Yes | Capgo |
| Ionic (on Capacitor) | Same as Capacitor | Yes | Capgo |
| Cordova | JavaScript in WKWebView | Yes | Capgo after migrating to Capacitor, legacy Cordova plugins |
| React Native / Expo | JavaScript bundle on Hermes or JavaScriptCore | Yes, JavaScript only | Expo Updates, self-hosted CodePush alternatives |
| NativeScript | JavaScript on an embedded engine | Yes, JavaScript only | Limited, mostly self-built |
| Flutter | Dart compiled AOT to machine code | Only through an interpreter | Shorebird (Dart interpreter for patches) |
| Native Swift / Objective-C | Machine code | No for native code; yes for JavaScript in WebKit or JavaScriptCore | Remote config and server-driven UI |
| .NET MAUI / Xamarin | C# compiled AOT to machine code | No | Remote config only |
| Kotlin / Compose Multiplatform | Kotlin/Native compiled to machine code | No | Remote config only |
| Unity / Unreal | C# via IL2CPP or C++, compiled | No for code, yes for content | Asset bundles, scripted content (Lua) |
| Web wrapper (remote URL) | Website loaded in a WebView | Always live, but review risk | None needed, see note below |
Capacitor, Ionic and Cordova: fully covered
Your app’s web UI and JavaScript business logic run in WKWebView, while the native shell and plugins remain native. Apple’s Developer Program License Agreement section 3.3.1(B) permits downloaded interpreted code only when it preserves the app’s primary purpose, does not create a code storefront, and does not bypass OS security. Guideline 4.7 separately governs apps offering HTML5 and JavaScript mini apps and adds additional requirements.
A live update replaces the web bundle. The native shell, plugins and permissions stay the ones Apple reviewed. That is why Capgo is compliant by design: it cannot change native code even if you wanted it to. New native plugins or permissions still need an App Store release.
React Native and Expo: JavaScript yes, native modules no
React Native runs your JavaScript bundle on Hermes or JavaScriptCore. That bundle is interpreted code and can be updated over the air, which is what Expo Updates and Microsoft CodePush (retired in 2025) were built for. Teams leaving CodePush need a replacement.
Any change to native modules, the React Native version, or Info.plist needs a new binary.
Flutter: only through an interpreter
On iOS, Flutter compiles Dart ahead of time to ARM machine code. Downloading new machine code is forbidden, so stock Flutter cannot use live updates. Shorebird solves this by running patched Dart code in an interpreter, which brings it under the interpreted code exception. Read more in live updates for Flutter apps.
Native Swift, .NET MAUI, Kotlin Multiplatform: no native code updates
These stacks produce native machine code. They cannot download replacement native code. A native app may still download and execute interpreted code, such as JavaScript through JavaScriptCore, when it meets Apple’s conditions. What you can also do over the air is change data: remote config, feature flags for features already reviewed, server-driven UI, images and copy.
Unity and game engines: content yes, code no
Game code is compiled. Asset bundles (levels, art, balancing data) are content and can be downloaded. Embedded scripting languages like Lua are interpreted code and follow the same rules as JavaScript.
Remote URL wrappers: live, but risky for review
An app that only loads your website in a WebView is “updated” every time you deploy the site. The problem is not guideline 2.5.2 but guideline 4.2 (minimum functionality): Apple rejects apps that are just a website in a frame. Bundling the web app with Capacitor, adding native features, and using live updates avoids that.
What no stack can do on iOS
Whatever your framework, a live update cannot:
- Download native code, frameworks or dynamic libraries
- Add new permissions or entitlements
- Change the app into something the App Store listing does not describe
- Turn on features that were hidden from App Review
Where to go next
- Android answer: Does Google allow live updates without Play Store review?
- Enforcement in detail: How Apple enforces OTA update restrictions
- Approval workflow: Capacitor OTA updates: App Store approval guide
- Real rejection stories: App Store refusal horror stories
Ship fixes today, not after review
Capgo live updates push JavaScript, HTML and CSS changes to your Capacitor app in minutes, with channels, rollbacks, delta updates and end-to-end encryption. It is open source and can be self-hosted.
bunx @capgo/cli@latest init
Follow the quickstart to send your first update.