Checking For and Applying Updates

Call the OTA Update SDK from your React Native app: check for updates, show progress, and control when a new bundle is applied.

For most apps this is the entire integration — one import, one wrapper:

App.tsx
import { withOtaUpdate } from '@otaupdate/react-native';

function App() {
  return <YourApp />;
}

export default withOtaUpdate(App);

That gives you, with no further code:

  • a check on app launch and on every return to the foreground (at most one per minute);
  • a background download, with the SHA-256 verified natively before anything is unzipped;
  • installation on the next cold start, so a user is never interrupted;
  • notifyAppReady() on mount — which is what arms rollback protection.

notifyAppReady() is not optional

Every freshly installed bundle is treated as unproven. If your app does not reach notifyAppReady() before the next launch, the native layer assumes the update is broken, reverts to the previous bundle and blacklists the bad hash. withOtaUpdate and startAutoSync call it for you. If you wire things up by hand, you must call it yourself — after your critical startup path, but not so late that a user can sit on a broken screen without it firing.

Tuning the automatic behaviour

Pass options to change when it checks and how it installs:

App.tsx
import { withOtaUpdate, InstallMode } from '@otaupdate/react-native';

export default withOtaUpdate(App, {
  checkOnAppStart: true,        // default
  checkOnResume: true,          // default
  minimumSyncInterval: 60,      // seconds between automatic syncs; default 60
  sync: {
    installMode: InstallMode.ON_NEXT_RESTART,   // default
    mandatoryInstallMode: InstallMode.IMMEDIATE, // default for mandatory releases
  },
});

Not using the HOC — a non-React entry point, or your own bootstrap sequence? Call startAutoSync directly; it takes the same options and returns a teardown function.

bootstrap.ts
import { startAutoSync, InstallMode } from '@otaupdate/react-native';

const stop = startAutoSync({ sync: { installMode: InstallMode.ON_NEXT_RESUME } });

Manual control

sync() is check → download → install in one call. It is safe to call whenever you like: concurrent calls share the run in flight rather than downloading twice, and it resolves with a SyncStatus instead of throwing.

anywhere.ts
import OtaUpdate, { InstallMode, SyncStatus } from '@otaupdate/react-native';

const status = await OtaUpdate.sync({
  installMode: InstallMode.ON_NEXT_RESTART,
  mandatoryInstallMode: InstallMode.IMMEDIATE,
  onSyncStatusChange: (s) => console.log(SyncStatus[s]),
  onDownloadProgress: ({ receivedBytes, totalBytes }) =>
    console.log(Math.round((receivedBytes / totalBytes) * 100) + '%'),
  // Ask the user first. Mandatory releases ignore the answer.
  shouldInstall: (update) => confirmWithUser(update.description),
});

if (status === SyncStatus.UPDATE_INSTALLED) {
  // Applied on the next restart — or call restartApp(true) to apply it now.
}

Update UI with the hook

useOtaUpdate exposes the same machinery as React state, for a "new version available" banner or a manual Check for updates button in settings.

UpdateBanner.tsx
import { useEffect } from 'react';
import { Button, Text, View } from 'react-native';
import { useOtaUpdate } from '@otaupdate/react-native';

export function UpdateBanner() {
  const { available, progress, isSyncing, check, update } = useOtaUpdate();

  useEffect(() => { void check(); }, [check]);
  if (!available) return null;

  return (
    <View>
      <Text>Version {available.label} is available</Text>
      {progress && <Text>{Math.round((progress.receivedBytes / progress.totalBytes) * 100)}%</Text>}
      <Button title="Update" onPress={() => update()} disabled={isSyncing} />
    </View>
  );
}

Building your own UI this way does not replace rollback protection — keep withOtaUpdate (or your own notifyAppReady() call) at the root.

Showing the running version

About.tsx
import { getCurrentPackage } from '@otaupdate/react-native';

const pkg = await getCurrentPackage();
// null when the app is running the bundle shipped in the binary.
// Otherwise: { label: '#12', packageHash, isPending, isFirstRun, appVersion, … }