Test an add-to-app patch before publishing it
In an add-to-app setup, the host app (Android or iOS) is often built by another
team, and a full QA build can take a while. You don’t need one to try a patch.
Build the host app yourself against the module release, publish the patch to
your own track, and only promote it to stable once it
works.
This page assumes you have already followed the Android or iOS add-to-app guide.
How it works
Section titled “How it works”Patches published with --track <name> reach only devices that ask for that
track. Your users’ devices ask for stable, so a patch on any other track is
invisible to them. Your module chooses its track at runtime with
package:shorebird_code_push,
and the host app tells it which track to use when it starts the Flutter engine.
A host app you build locally asks for your track. The host app you ship passes
no track, so it keeps checking the default track (stable, or the channel:
set in shorebird.yaml).
Set up the module once, before you release
Section titled “Set up the module once, before you release”The code that chooses the track has to be in the release, because a patch can’t
reach a device until the device asks for its track. Make these changes before
you run shorebird release aar or shorebird release ios-framework.
-
Add
shorebird_code_pushto your Flutter module: -
Turn off the automatic update check in the module’s
shorebird.yaml, so that your code decides which track to check: -
Read the track from the entrypoint arguments in the module’s
main, and check for updates on it. With no argument,trackisnulland the updater checks the default track, so the host app you ship needs no change. Requiresshorebird_code_push2.0.4 or newer.Because
auto_updateis off, thisupdatecall is what checks for patches in every build, including the one your users run. Don’t remove it. If the host app starts the module through other Dart entrypoints (functions marked@pragma('vm:entry-point')), callcheckForPatchfrom each of them too, or those engines never check for patches.updatethrows when the check fails, for example when the device is offline. ThecatchErrorkeeps that from reaching your error reporting as an unhandled error.
Then create the release as usual:
Build the host app locally with your track
Section titled “Build the host app locally with your track”Build the host app the way the add-to-app guide describes, from the module’s
release directory: on Android it’s the local Maven repository with the aar,
on iOS it holds the .xcframeworks. Three things matter:
- Use the exact release. You need the
releasedirectory thatshorebird releaseproduced (from your CI, or from whoever ran it). If you rebuild the module withflutter build, the result doesn’t match the release and patches won’t apply to it. - Use a release build. Shorebird only patches release builds: the Android
releasebuild type, or the iOSReleaseconfiguration on a device (not a simulator). - Keep the version. The host app’s version must match the
--release-versionyou released with, or the device asks for patches to a release that doesn’t exist.
Pass your track to the module when the host app starts the Flutter engine. Pick
a name that is yours, such as dev-<your-name>.
On Android, add a build config field that is empty unless you set a Gradle property:
On iOS, add a user-defined build setting SHOREBIRD_TRACK (empty by default) to
your app target, and an Info.plist entry ShorebirdTrack with the value
$(SHOREBIRD_TRACK).
Then pass the track to the module only when it’s set:
If your Android app starts Flutter another way, pass the same list to
FlutterActivity.withNewEngine().dartEntrypointArgs(...),
FlutterFragment.withNewEngine().dartEntrypointArgs(...), or
FlutterEngineGroup.Options.setDartEntrypointArgs(...).
Build and install your local host app with the track set:
installRelease exists only when the release build type has a
signingConfig. If your host app signs release builds only in CI, sign your
local build with the debug key by adding signingConfig signingConfigs.debug to
the release build type, and don’t commit that change. On iOS, devicectl
needs Xcode 15 or newer and a device on iOS 17 or newer. Run
xcrun devicectl list devices to find the device ID.
Publish the patch to your track and try it
Section titled “Publish the patch to your track and try it”Make your change in the module, then publish it to your track:
The track is created the first time you publish to it. Launch your local build
so the module checks for the patch. If your app starts the Flutter engine only
when a Flutter screen opens, open one. Then fully quit the app (force-stop it on
Android) and launch it again to run the patch. In logcat or the Xcode console,
the patch check names your track and your release version (1.2.3+4 in the iOS
example):
To try another change, publish another patch to the same track.
Promote the patch when it works
Section titled “Promote the patch when it works”When the patch works, move it to stable without rebuilding. Your users get the
exact patch you tested. Run this from the module directory, with the patch
number that shorebird patch printed (or find it with
shorebird patches list --release-version <version>):
Promoting moves the patch from your track to stable.
See Tracks for more on tracks, including how to list and delete them.