Applying updates
Applying updates
After Patch downloads an update, install mode controls when that bundle becomes active. Non-mandatory releases wait for the condition set in installMode. Mandatory releases (published with --mandatory) use mandatoryInstallMode instead and do not wait for that looser timing.
For when the app checks and downloads updates, see Checking for updates.
Install modes
installMode sets when a non-mandatory release becomes active: the client waits for restart, background, or resume (see table). Mandatory releases skip that path and use mandatoryInstallMode instead (default: IMMEDIATE).
| Install mode | When the new bundle becomes active |
|---|---|
ON_NEXT_RESTART | Next cold start (default installMode) |
ON_NEXT_SUSPEND | When the app goes to background after minimumBackgroundDuration |
ON_NEXT_RESUME | When the app returns to foreground after minimumBackgroundDuration |
IMMEDIATE | As soon as install finishes (default mandatoryInstallMode) |
ON_NEXT_RESTART is the default installMode and works well for Staging, where developers restart the app on purpose. In Production, an update applying on restart can cause unwanted behavior. ON_NEXT_SUSPEND is a common installMode for Production: the bundle activates when the user backgrounds the app after minimumBackgroundDuration, without interrupting an open session.
void sync({
installMode: "ON_NEXT_SUSPEND",
mandatoryInstallMode: "IMMEDIATE",
minimumBackgroundDuration: 60_000, // ms; for ON_NEXT_RESUME / ON_NEXT_SUSPEND
});
minimumBackgroundDuration
For ON_NEXT_RESUME and ON_NEXT_SUSPEND, Patch only activates a pending update if the app was in the background for at least this many milliseconds. That avoids restarting someone who briefly switched to another app.
Does not change behavior for ON_NEXT_RESTART or IMMEDIATE.
Different behavior per deployment
Sync and apply behavior is client-side. The server does not push different sync() rules per deployment.
Teams can map Staging and Production to different app builds (different deployment keys baked into native config), then branch sync() options on the same build-time signal that selects the deployment key:
// Wire this to whatever selects your Staging deployment key at build time:
// a build flavor / scheme, or a build-time env flag (e.g. react-native-config).
declare function isStagingBuild(): boolean;
void sync(
isStagingBuild()
? { installMode: "ON_NEXT_RESTART", mandatoryInstallMode: "IMMEDIATE" }
: {
installMode: "ON_NEXT_SUSPEND",
mandatoryInstallMode: "ON_NEXT_RESUME",
minimumBackgroundDuration: 300_000, // 5 min
},
);
This matches the common pattern: ON_NEXT_RESTART in Staging, ON_NEXT_SUSPEND in Production.
Mandatory updates
Publishing with --mandatory (CLI or dashboard) marks a release mandatory on the server. Eligible clients install it under mandatoryInstallMode rather than waiting for installMode conditions.
On the client, mandatoryInstallMode controls when that apply happens (default: IMMEDIATE, as soon as the download finishes).
You can toggle mandatory on an existing release with cmpatch release patch or promote with no new app binary required. See Production control.
If a device skipped an older mandatory release, a later compatible update may still be treated as mandatory until the user catches up.
UX hook points
Patch does not ship built-in prompts, splash transitions, or update dialogs. Your app decides the UX.
Common hook points:
sync()result ("update-installed","up-to-date","error") to decide whether to show messaging- progress callback (
receivedBytes,totalBytes) for progress UI checkForUpdate()+remotePackage.releaseNotesfor "update available" textdisallowRestart()/allowRestart()to avoid restarts during critical flows
For button-driven flows (check now, download now, apply now), see Manual control.
Splash screen timing
If you apply updates around app launch, treat splash behavior as app UX policy:
- defer apply to next restart/resume for less disruptive startup
- or keep splash visible while checking/downloading when fast-first-update UX matters more
Patch exposes install timing and restart controls; your app owns the splash implementation details.
Blocking restarts during critical UX
disallowRestart() prevents Patch from programmatically reloading the JS bundle (including after IMMEDIATE installs) until allowRestart() runs. Typical pattern: block during checkout or onboarding, allow when the flow completes.
import { disallowRestart, allowRestart } from "@codemagic/react-native-patch";
disallowRestart();
// … critical flow …
allowRestart(); // may flush a queued restart
Alternatively, prefer ON_NEXT_RESTART and call restartApp() only when your app knows it is safe. disallowRestart is for global sync() calls where you still need a no-interruption window.
Summary
| Goal | Primary API |
|---|---|
| Less intrusive apply timing | installMode, minimumBackgroundDuration |
| Critical fixes without waiting for install conditions | Server --mandatory; client mandatoryInstallMode |
| No restart during checkout, etc. | disallowRestart / allowRestart |
| Full control over each step | Manual control |