HotupdaterHot Updater
Deliver updates

Runtime Channel Switch

Temporarily move a device to another channel at runtime, then return to the default channel when needed.

Overview

Runtime channel switch lets a built app temporarily use a bundle from another channel without changing the default channel embedded in the binary.

  • The existing deployment process does not change.
  • hotUpdater.checkForUpdate({ channel }) checks another channel.
  • The switch is persisted only after updateInfo.updateBundle() succeeds.
  • You cannot jump directly from one switched runtime channel to another. Reset back to the default channel first.
  • hotUpdater.resetChannel() removes the switch and restores the original bundle.

Set your app's default channel first. See Default Channel Setup. Runtime channel switching is explicit. It only starts when you pass a specific channel to hotUpdater.checkForUpdate({ channel: "..." }). Calling hotUpdater.checkForUpdate() without channel keeps the existing update flow for the app's current channel.

Use this runtime channel switch flow when you want to opt a device into another channel without rebuilding the native app:

  1. Keep a build-time default channel such as production.
  2. Check another channel with hotUpdater.checkForUpdate({ channel: "beta" }).
  3. Call updateInfo.updateBundle() only when an update is returned.
  4. Reload the app to start using the installed bundle.
  5. Use hotUpdater.getChannel(), hotUpdater.getDefaultChannel(), and hotUpdater.isChannelSwitched() to show the current state in your UI.
  6. When you want to leave the runtime channel switch, call hotUpdater.resetChannel() and reload again.

This keeps the full lifecycle explicit:

  • Opt in with checkForUpdate({ channel }) + updateBundle() + reload()
  • Observe state with getChannel() + getDefaultChannel() + isChannelSwitched()
  • Opt out with resetChannel() + reload()

Opt Into Another Channel

updates.ts
import { hotUpdater } from "./hotUpdater";

async function enableBetaChannel() {
  const updateInfo = await hotUpdater.checkForUpdate({
    updateStrategy: "appVersion",
    channel: "beta", 
  });

  if (!updateInfo) {
    console.log("No beta bundle available");
    return false;
  }

  const installed = await updateInfo.updateBundle();

  if (!installed) {
    return false;
  }

  await hotUpdater.reload();
  return true;
}

Show Current State

import { Text, View } from "react-native";

import { hotUpdater } from "./hotUpdater";

function ChannelStatus() {
  const currentChannel = hotUpdater.getChannel();
  const defaultChannel = hotUpdater.getDefaultChannel();
  const switched = hotUpdater.isChannelSwitched();

  return (
    <View>
      <Text>Current channel: {currentChannel}</Text>
      <Text>Default channel: {defaultChannel}</Text>
      <Text>Runtime switch active: {switched ? "yes" : "no"}</Text>
    </View>
  );
}

Reset to the Default Channel

import { hotUpdater } from "./hotUpdater";

async function restoreDefaultChannel() {
  if (!hotUpdater.isChannelSwitched()) return;

  const reset = await hotUpdater.resetChannel();
  if (reset) await hotUpdater.reload();
}

If the app is already switched to a runtime channel such as beta, do not try to jump directly to another runtime channel such as alpha. Call hotUpdater.resetChannel() and reload first, then start a new checkForUpdate({ channel: "..." }) flow from the default channel.

Important Behavior

  • The existing deployment process is unchanged.
  • checkForUpdate({ channel: "beta" }) only checks the beta channel.
  • hotUpdater.checkForUpdate() without a channel does not trigger runtime switching.
  • The app does not switch channels just by checking.
  • If no update is returned, the app stays on the current channel.
  • After updateInfo.updateBundle() succeeds, hotUpdater.getChannel() returns the selected runtime channel and hotUpdater.isChannelSwitched() reflects it, even before reload. Reload to start the selected code.
  • hotUpdater.getDefaultChannel() still returns the embedded default channel.
  • If a runtime channel switch is already active, checkForUpdate({ channel: "..." }) cannot move directly to a different runtime channel. Call hotUpdater.resetChannel() before starting another switch.
  • hotUpdater.resetChannel() clears downloaded OTA state even on the default channel. After reloading, the app runs its built-in bundle. Guard with hotUpdater.isChannelSwitched() if the action should only leave an active switch, as in the example above.

While a runtime switch is active, checking any different channel, including the embedded default channel, rejects before requesting a catalog. Call hotUpdater.resetChannel() and reload before checking another channel.

On this page