HotCodePush

Choose an apply strategy

Compare next_start, immediate, next_resume and manual, set the strategy a mandatory release follows, and handle the reload in code.

An apply strategy decides when a downloaded update becomes the running bundle. The choices are the next start, at once, the next resume, or the moment your code picks. HotCodePush reads it as applyStrategy in hotcodepush.json, with mandatoryApplyStrategy for releases marked mandatory.

The options table on your SDK's configuration page, such as the Capacitor configuration, lists every key with its type and default. This guide picks the strategy and shows the code around the reload.

Prerequisites

1. Pick the strategy

StrategyWhen the reload happensWhat the user seesPick it when
next_start, the defaultThe reload happens at the next cold start.The user sees nothing. The next start runs the new bundle.Pick it for most apps, where an update may wait until the user opens the app again.
immediateThe reload happens as soon as the download is verified and the app is up.The app reloads under their hands, once per update.Pick it when a fix must land now, and the app holds restarts with setRestartAllowed() where it has state to protect.
next_resumeThe reload happens on the first resume after at least nextResumeMinimumBackgroundSeconds in the background, 300 by default.The user sees a reload at the moment they return.Pick it for apps used in short sessions.
manualThe reload happens when the app calls applyUpdate().The user sees whatever your code shows, such as a "restart now" button.Pick it for apps with flows to protect, kiosks, and any app that wants to choose the moment.

A reload replaces the running JavaScript: in-memory state is gone, and the app starts on the new bundle as after a cold start. After the reload the readiness gate runs. A bundle that does not render in time is rolled back, as Roll back a release covers.

A release created with --mandatory ignores applyStrategy and follows mandatoryApplyStrategy: immediate by default, or manual to hand the update to your code, which reads isMandatory on the release and calls applyUpdate(). There is no next_start for a mandatory release. Mandatory is transitive over skipped releases: a device that skipped a mandatory release it was eligible for treats the release it finally takes as mandatory.

sync() and downloadUpdate() take applyStrategy and mandatoryApplyStrategy for one cycle: await HotCodePush.sync({ applyStrategy: "immediate" }) applies this update at once whatever the file says. The moment is decided at download time and stored with the downloaded release.

2. Set it in hotcodepush.json

If the default next_start fits and you need no code around the reload, skip to step 4.

hotcodepush.json
{
  "appId": "2f1c9a7e-5b3d-4e8a-9c21-7d4f6a0b8e13",
  "channel": "production",
  "applyStrategy": "next_resume",
  "nextResumeMinimumBackgroundSeconds": 300,
  "mandatoryApplyStrategy": "manual"
}

A change to the file reaches the app through a build. Devices on binaries built before the change keep the old strategy until a store release. The per-call override above changes the moment without a build.

3. Handle the moment in code

Under applyStrategy: manual the SDK keeps the download and tells you through updateDownloaded, and your code applies it when the moment is right. A mandatory release under mandatoryApplyStrategy: manual arrives through the same event, and release.isMandatory tells your code that the user may be told but not asked:

src/updates.ts
import { HotCodePush } from "@hotcodepush/capacitor-live-updates";

let pendingBundleVersion: string | null = null;

void HotCodePush.addListener("updateDownloaded", async ({ release, applyStrategy }) => {
  if (applyStrategy !== "manual") {
    return;
  }
  if (release.isMandatory) {
    // Tell the user that version release.bundleVersion restarts the app, then apply.
    await HotCodePush.applyUpdate();
    return;
  }
  pendingBundleVersion = release.bundleVersion;
  // Show your "Restart to update" control here; it calls applyPendingUpdate().
});

export function getPendingBundleVersion(): string | null {
  return pendingBundleVersion;
}

export async function applyPendingUpdate(): Promise<void> {
  const { status } = await HotCodePush.applyUpdate();
  // applied: the app reloads right after this line. nothing_to_apply: nothing waits any more.
  if (status !== "applied") {
    pendingBundleVersion = null;
  }
}

Under immediate, next_resume or for a mandatory release, setRestartAllowed({ isAllowed: false }) holds the restart while the user is busy. The download goes on, and only the reload waits. The flag is not persisted: every start begins allowed.

src/checkout.ts
import { HotCodePush } from "@hotcodepush/capacitor-live-updates";

export async function startCheckout(): Promise<void> {
  await HotCodePush.setRestartAllowed({ isAllowed: false });
}

export async function finishCheckout(): Promise<void> {
  // A restart the SDK held during the checkout runs now.
  await HotCodePush.setRestartAllowed({ isAllowed: true });
}

4. Check it on a device

Build the app with the new file, release a bundle to the channel the build follows, and open the debug screen on the device with HotCodePush.showDebugScreen(). It shows the running release and the downloaded one with the last check's status and reason.

Under next_start, next_resume and manual the downloaded release sits beside the running one until its moment. Under immediate the running release is the new one already. If nothing happens, see Troubleshooting.

Next steps

  • Roll back a release: a rollback is mandatory by default, which is where mandatoryApplyStrategy decides who restarts the app.
  • Configuration is the React Native SDK's options table. The Expo, Capacitor and Cordova pages list the same keys.
  • API documents sync(), applyUpdate(), setRestartAllowed() and the events with their result types.

Last updated on

On this page