Skip to main content

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 modeWhen the new bundle becomes active
ON_NEXT_RESTARTNext cold start (default installMode)
ON_NEXT_SUSPENDWhen the app goes to background after minimumBackgroundDuration
ON_NEXT_RESUMEWhen the app returns to foreground after minimumBackgroundDuration
IMMEDIATEAs 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.

info

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.releaseNotes for "update available" text
  • disallowRestart() / 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

GoalPrimary API
Less intrusive apply timinginstallMode, minimumBackgroundDuration
Critical fixes without waiting for install conditionsServer --mandatory; client mandatoryInstallMode
No restart during checkout, etc.disallowRestart / allowRestart
Full control over each stepManual control