Store Compliance
Over-the-air updates are allowed by both the App Store and Play Store — within limits. Code Push is built to stay inside those limits, but compliance also depends on what you ship and how. This page explains the rules, how Koolbase is designed around them, and how to use Code Push without putting your app at risk.
The one rule to remember
Why this is allowed at all
Both Apple and Google generally prohibit downloading and running native executable code (Swift/Objective-C, Kotlin/Java) outside their review process. What they permit is interpreted code that runs inside a virtual machine with limited access to the OS.
That distinction is the whole basis of Flutter code push. Your Dart runs inside the Dart VM — not as native machine code with direct system access. Koolbase only ever touches that Dart layer. It never modifies native binaries, plugins, permissions, or the engine. Because the updated code stays inside the VM, it falls within the path the stores allow. This is the same basis the broader Flutter and React Native code-push ecosystem operates on.
Koolbase never ships native code
What code push can and can't change
A VM patch replaces your app's compiled Dart code — nothing else. Images, fonts, and other bundled assets live in the Flutter asset bundle, not the Dart snapshot, so a patch can't add or change them. If patched code references a bundled asset that isn't already in the shipped build, it won't load.
Patchable over the air:
- UI layout, screens, widgets, navigation, and strings
- Business logic, state, and theming — any Dart code
- Dart dependencies, as long as they add no native code
- Which remote image or font your code loads (see below)
Requires a normal store release:
- Bundled assets — images, fonts, anything loaded via
rootBundle/Image.asset - Native code (Kotlin/Java, Swift/Objective-C) and plugins that ship it
- Permissions and manifest changes
- The Flutter engine / SDK version
Working around the asset limit
cdn.koolbase.com URL — then a patch that swaps the URL, or a Remote Config change, updates the asset live. The asset travels over the network at runtime, so it never needs to be in the patch.Google Play
Play's Device and Network Abuse policy prohibits self-updating outside the Play Store and downloading unauthorized executable code, but explicitly allows code that runs in a VM with limited API access — which is where Dart runs. The policy's real test is that dynamically delivered code must not enable a violation of Play policies.
Enforcement is intent-based: good-faith code that isn't doing anything malicious is treated as ordinary app code, not as a backdoor. In practice this means Android code push for bug fixes sits in well-established, accepted territory.
Apple App Store
Apple's guideline 2.5.2 is stricter. Apps may not download or execute code that changes features or functionality, with a narrow allowance for interpreted code that (a) does not change the app's primary purpose, (b) does not create a store for other code, and (c) does not bypass the OS's signing or sandbox protections.
The safe envelope on iOS is therefore narrower than on Android: ship bug fixes that keep your app doing the same thing it was reviewed to do. Don't use code push to introduce a materially new feature on iOS — that belongs in a store release. Apple does actively enforce 2.5.2.
iOS code push availability
Android ABI coverage
Koolbase code-push currently ships engines for arm64-v8a and armeabi-v7a — together these cover effectively every physical Android phone in the field. x86_64 (used by emulators and a small number of devices) is not yet supported.
Because your plugins may bundle x86_64 native libraries, the AAB that koolbase release android produces can contain an x86_64 split with no Koolbase engine behind it — a device on that split would run without code-push. Before uploading to Play, restrict your build to the supported ABIs:
Groovy (android/app/build.gradle):
// android/app/build.gradle
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a'
}
}
}Kotlin DSL (android/app/build.gradle.kts):
// android/app/build.gradle.kts
android {
defaultConfig {
ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a")
}
}
}x86_64 support is on the way
x86_64, this step goes away: the bundle will carry a code-push-capable x86_64 split and you can drop the abiFilters restriction. Until then, restricting ABIs ensures no device is ever served a split without code-push.Staying compliant in practice
Do:
- Use Code Push for bug fixes, crash fixes, and tuning config/flags
- Keep releasing to the stores periodically — new users install from there, not from a patch
- Keep updates honest: don't do anything a user wouldn't expect from your app's stated purpose
- Ship larger feature work through a normal store release, then use patches to fix it
Don't:
- Use Code Push to introduce a major new feature solely to skip store review
- Change the app's primary purpose from what was reviewed and advertised
- Ship anything deceptive, or that the store listing wouldn't lead a user to expect
- Treat code push as a permanent replacement for store releases
Why keep shipping store releases
Code Push only updates an app after it's installed. New users always get the store build first, so that build should be current. Continuing to release periodically — to refresh screenshots, ship native changes, and keep your reviewed build in sync with reality — is both good practice and part of staying compliant. Think of Code Push as the fast lane between releases, not a replacement for them.