iOS
The whole iOS flow, from an app that has never used OverPush to a fix running on your users' iPhones. Works with App Store and TestFlight builds.
Before you start
- A Mac with Xcode, Apple silicon or Intel.
- A Flutter app that builds for iOS in release mode, and the Apple developer account you already ship it with.
- Flutter 3.41.4, 3.47.5, 3.47.6, 3.47.7, 3.47.8 or 3.47.9. iOS patching needs build tools made for your exact Flutter version; these are available today (see Flutter versions). Flutter 3.47 needs CLI 1.2.15 or later. On any other version the CLI says so before building anything.
- The OverPush CLI, installed and logged in. See Getting started.
- An app created in the console. The same app id is used for iOS and Android.
1. Set up the project
-
Add the package
$ flutter pub add overpush -
Connect the project
From the project root, the folder with
pubspec.yaml:$ overpush init --app-id cp_your_app_idThis writes
overpush.yamland stores a project key in.overpush/key, which is added to.gitignorefor you. You don't changemain.dart,AppDelegate,Info.plistor your Xcode project.
2. Ship a release
-
Try it on your iPhone first (recommended)
With an iPhone connected over USB:
$ overpush release ios --artifact app $ overpush preview ios
The first command builds the release as an app for your own devices, signed with your development certificate; no distribution certificate needed. The second installs it on the iPhone and opens it. The first iOS build on a computer also downloads the build tools for your Flutter version, once.
-
Build the App Store release
$ overpush release iosThis builds the IPA App Store Connect takes, and records the release so it can be patched. To try patches on exactly the build your users get, install it from TestFlight and check it with
overpush verify ios --installed.Patches only reach iPhones running a build made by
overpush release. A build from plainflutter build iparuns normally but never receives patches. -
Upload it to App Store Connect
Three commands, for three different jobs. Use any one:
Command What it does overpush releases archive(easiest)Saves the newest release's files to your account, then opens Xcode's Organizer. overpush releases archive 1.4.0+31The same, for a release you name by its version. overpush distribute iosOnly opens Xcode's Organizer. overpush upload iosUploads the IPA without Xcode, with an App Store Connect API key or an Apple ID and app-specific password. The CLI suggests
releases archiveafter a release because it does both jobs at once.overpush release ioshas usually saved the files already (it prints "Uploading release files"), sodistribute iosis enough then.With
releases archiveordistribute, Xcode's Organizer opens on this release's build. In it:- Select the archive the CLI names.
- Click Distribute App.
- Choose App Store Connect, then Upload.
overpush release iosalready saves the release to your account, so any computer logged in to it can patch it. Runreleases archivewhen that step printed a warning, or for a release built before it did this. Add--no-distributeto skip opening Xcode.Uploaded it some other way, for example with Transporter? Tell OverPush, so the console knows which build is in the store:
$ overpush releases mark-uploaded latestThen send it to TestFlight and App Review as usual.
3. Publish a patch
-
Change some Dart
Fix the bug, change a screen, add a new one. See what a patch can change.
-
Check it first (optional)
$ overpush patch ios --dry-runBuilds and checks the patch without publishing anything.
-
Publish
$ overpush patch iosThe CLI builds the patch for the newest release, checks that nothing outside Dart changed, and publishes it. To start with a share of users:
$ overpush patch ios --rollout-percent 10 -
See it on the iPhone
Open the app. A few seconds after it starts, it downloads the patch in the background. Swipe the app away in the app switcher and open it again, and the patched code runs. Sending it to the background and back isn't enough.
To check from the Mac what the app on a connected iPhone did, including TestFlight and App Store installs:
$ overpush verify ios --installed -
Watch it, then widen or stop it
The release page in the console shows each patch with its installs and failures. Then:
$ overpush patches rollout 1 --percent 100 # everyone $ overpush patches revoke 1 --reason "wrong total" # stop it everywhere $ overpush patches rollback --to 1 # serve patch 1's code again
If the project also has Android releases, add
--platform ios. All of these are also in the console's patch menu. See Rollouts & rolling back.
What a patch can change
| Change | As a patch? |
|---|---|
| Dart code: logic, widgets, layouts, text, new screens, new classes | ✅ Yes |
| Pure-Dart packages (no native code) | ✅ Yes |
Swift or Objective-C, Info.plist, entitlements, the Xcode project, Pods | ❌ Needs an App Store release |
| Adding, removing or upgrading a plugin with native code | ❌ Needs an App Store release |
| Images, fonts and other assets | ❌ Needs an App Store release |
Before publishing, the CLI compares the project with the release: files under ios/, the iOS side of every plugin (including a plugin you upgraded without touching ios/), and assets. If anything changed, it stops and says what. --allow-native-diffs and --allow-asset-diffs publish anyway, only for changes you're sure don't matter, such as a comment.
Shipping a new App Store version
Bump the version or build number in pubspec.yaml and run overpush release ios again. iPhones that update from the App Store start clean: patches made for the old version don't apply to the new one, and new patches go to the new release.
Flutter upgrades
A patch must be built with the same Flutter version as its release. After you upgrade Flutter, ship a new App Store release before patching again; the CLI won't build a patch that doesn't match its release. Check that the new version is supported for iOS first (see Before you start).
Obfuscation
Add obfuscate: true to overpush.yaml and release again. Patches for that release are obfuscated the same way automatically. See Configuration.
What to expect
| Speed | Code you don't change runs at full speed. Code you change in a patch can run a little slower; for typical UI and business logic you won't notice. For heavy work such as image processing or large calculations, ship the change in a store release. |
| Patch size | The first patch for a release is usually a couple of MB. Every patch after that is usually tens of KB, because a phone only downloads what changed since the patch it has. |
| When it applies | On the next cold start after the download finishes. |
| A bad patch | If a patch stops the app from starting, the iPhone removes it and runs the App Store build again on its own. See Automatic rollback. |
App Store review
Apple's guidelines (section 3.3.2) allow apps to download code that changes behaviour, as long as it doesn't change the app's primary purpose or add features that bypass review. OverPush patches never contain native code.
Use patches for fixes and improvements. Don't use them to turn your app into something App Review didn't see.
TestFlight
TestFlight builds receive patches like App Store builds. A good habit: send a release to TestFlight, publish a patch, and check it on a TestFlight install with overpush verify ios --installed before the release goes live.
Signing
OverPush doesn't need any signing setup beyond what you already use for flutter build ipa. Patches are signed by OverPush when you publish them; there's no key for you to manage.
Common problems
"No OverPush engine published for Flutter X"
Your Flutter version isn't supported for iOS yet. Use a supported version (see Before you start) for iOS releases.
The app shows a white screen after a patch
Usually the patch was built with a different Flutter version than the release. Revoke it with overpush patches revoke <n>, then see Troubleshooting.
"No device build is kept for this release"
overpush preview ios installs a release built with --artifact app. Run overpush release ios --artifact app first.
More in Troubleshooting.