Live Update — Events
Two events fire during the Live Update lifecycle. Subscribe with addListener(); the returned handle has a remove() method you call when you no longer need the listener (typically in your component unmount or app shutdown hook).
import { NativeUpdate } from 'native-update';
import type {
DownloadProgressEvent,
UpdateStateChangedEvent,
PluginListenerHandle,
} from 'native-update';
downloadProgress
Fires repeatedly while a bundle is downloading. The event interval is platform-dependent — typically every ~100 ms or every ~1 % progressed, whichever is less frequent — so it is safe to wire directly into a UI progress bar without throttling.
Payload
interface DownloadProgressEvent {
percent: number; // 0–100
bytesDownloaded: number; // bytes received so far
totalBytes: number; // total bundle size in bytes
bundleId: string; // identifies the bundle being downloaded
}
| Field | Type | Description |
|---|---|---|
percent | number | bytesDownloaded / totalBytes * 100. |
bytesDownloaded | number | Bytes received so far. |
totalBytes | number | Total bundle size, populated as soon as the response headers arrive. |
bundleId | string | Stable identifier of the bundle being downloaded. Lets you support multiple in-flight downloads. |
When it fires
- Once per progress tick during any bundle download (
download(),downloadUpdate(), or the auto-download triggered bysync()). - The final event for a bundle has
percent === 100. The next signal that the bundle is usable is theupdateStateChangedevent withstatus: READY.
Example
const handle: PluginListenerHandle = await NativeUpdate.addListener(
'downloadProgress',
({ percent, bundleId, bytesDownloaded, totalBytes }: DownloadProgressEvent) => {
setDownloadPercent(percent);
setDownloadBundleId(bundleId);
if (percent === 100) {
console.log(`[native-update] ${bundleId} download complete (${totalBytes} B)`);
}
},
);
// Later, on cleanup:
await handle.remove();
updateStateChanged
Fires whenever a bundle's BundleStatus changes — the most useful signal for "did the new bundle become active?".
Payload
interface UpdateStateChangedEvent {
status: BundleStatus; // PENDING | DOWNLOADING | READY | ACTIVE | FAILED
bundleId: string;
version: string;
}
| Field | Type | Description |
|---|---|---|
status | BundleStatus | The new state. |
bundleId | string | Bundle identifier. |
version | string | Semver of the bundle that changed state. |
Typical state sequence
PENDING → DOWNLOADING → READY → ACTIVE
A failed verification or cancelled download surfaces as a single transition to FAILED.
When it fires
| Transition | Triggered by |
|---|---|
→ PENDING | Server returned a new version; download has not started. |
→ DOWNLOADING | Download started (after download(), downloadUpdate(), or auto-download from sync()). |
→ READY | Bundle downloaded and verified. |
→ ACTIVE | applyUpdate(), set() + reload(), or the configured strategy applied the bundle. |
→ FAILED | Verification failed, download cancelled, or auto-rollback triggered because notifyAppReady() was not called on the previous boot. |
Example — render a "restart to apply" UI
const stateHandle = await NativeUpdate.addListener(
'updateStateChanged',
({ status, version }: UpdateStateChangedEvent) => {
if (status === 'READY') {
showSnackbar(`Update ${version} is ready — restart to apply.`);
}
if (status === 'FAILED') {
reportToSentry(new Error(`Bundle ${version} failed`));
}
},
);
Cleanup
The listener handle returned by addListener() carries a remove() method. Call it when the listener is no longer needed:
await handle.remove();
For a global "tear it all down" call (e.g. on logout, or in tests), use:
await NativeUpdate.removeAllListeners();
This removes every listener added via addListener() across all event types in the plugin (not just Live Update).
Common pitfalls
- Forgetting
remove()in React component unmount. Each render of a component that registers a listener stacks another subscription. Always remove in theuseEffectcleanup. - Setting state from the listener after unmount. Wrap the state setter in a "still mounted" check, or use a ref.
- Subscribing inside
sync()'s promise chain. Listeners must be active beforesync()is called or the earlyDOWNLOADINGevent can be missed. Subscribe at app boot.