Events & Listeners
The plugin emits lifecycle events through the standard Capacitor listener API. Events complement — they don't replace — the pull APIs check() and getState(): listeners tell you the moment something happens while your app is running, and the pull APIs reconcile anything that happened while it wasn't.
import { OtaKit } from '@otakit/capacitor-updater';
OtaKit.addListener('updateStaged', ({ bundle }) => {
// show "Update ready — restart now?" and call OtaKit.apply() on accept
});Event reference
A check found a newer bundle, before any download starts. Fires on manual check() and at the start of automatic flows.
A bundle was downloaded, verified, and staged, ready to apply. Download and stage are atomic in OtaKit, so this is the single 'downloaded' event.
A newly activated bundle was confirmed healthy — fires when notifyAppReady() confirms the trial bundle, in the reloaded JS context.
A download, verification, or extraction failed. Non-terminal: the app keeps running its current bundle and retries on a later cycle.
An applied bundle failed its health check (notifyAppReady timeout) and was reverted to the previous bundle.
Caveats
- Events fire only while the app process is alive. Nothing is delivered for work that happened while the app was killed or before your listener attached — there is no buffering or replay.
- Reconcile with
getState()andgetLastFailure()on startup. A bundle staged in a previous session shows up ingetState().staged, and a startup rollback (the app restarted beforenotifyAppReady()) happens before any JS runs, so it can only be observed viagetLastFailure(). apply()destroys the old JS context. A listener registered before the restart will not seeupdateApplied— that event fires in the reloaded bundle. AttachupdateAppliedandrollbacklisteners early in app startup, not inside the restart click handler.- One
updateStaged, no separate "downloaded" event. Download, hash verification, and staging are one atomic operation.
The apply timeline, end to end:
updateStaged (old context) -> user taps "Restart" -> OtaKit.apply() (old JS context ends here; WebView reloads) -> new bundle boots -> app calls notifyAppReady() -> updateApplied (NEW context - attach this listener at startup)
Use cases
From the smallest building block to the most hands-off setup — each pattern moves one more step of the flow from your code into the plugin.
1. Check for an update, download if the user says yes
The simplest pattern, no listeners at all — a "Check for updates" button in a settings screen, driven entirely by the pull API:
async function checkForUpdates() {
const result = await OtaKit.check();
if (result.kind === 'no_update') {
toast("You're up to date");
return;
}
// update_available or already_staged
if (await confirmDownload(result.latest.version)) {
const download = await OtaKit.download(); // no-op if already staged
if (download.kind === 'staged' && (await confirmRestart())) {
await OtaKit.apply(); // terminal: reloads into the new bundle
}
}
}2. Ask before every download
Same consent flow, but event-driven instead of a button — pair manual mode with updateAvailable so the user approves the download (useful on metered connections):
// capacitor.config.ts: { launchPolicy: 'off', resumePolicy: 'off' }
OtaKit.addListener('updateAvailable', async (latest) => {
if (await confirmDownload(latest.version)) {
await OtaKit.download(); // emits updateStaged when done
}
});
await OtaKit.check();3. Auto-download in the background, prompt to apply
The most hands-off custom flow: shadow mode does the checking, downloading, throttling, and deduplication, and your code only owns the restart prompt:
// capacitor.config.ts
plugins: {
OtaKit: { launchPolicy: 'shadow', resumePolicy: 'shadow' }
}// app startup
const state = await OtaKit.getState();
if (state.staged) showRestartPrompt(state.staged);
OtaKit.addListener('updateStaged', ({ bundle }) => showRestartPrompt(bundle));
// in the prompt's accept handler
await OtaKit.apply();4. Confirm and report after the fact
Works alongside any of the above (or the fully automatic default) — a toast when an update landed, and error reporting when one failed:
// attach at startup, in the reloaded bundle
OtaKit.addListener('updateApplied', ({ bundle }) => {
toast('Updated to ' + bundle.version);
});
OtaKit.addListener('downloadFailed', (e) => reportError('ota_download', e));
OtaKit.addListener('rollback', (e) => reportError('ota_rollback', e));
// startup rollbacks can't reach a listener - reconcile:
const failure = await OtaKit.getLastFailure();
if (failure) reportError('ota_rollback_startup', failure);Cleanup
addListener resolves to a PluginListenerHandle; call .remove() on it when the owning UI unmounts, or OtaKit.removeAllListeners() to drop everything at once.
Also see Update Strategies for choosing the policies these events observe, and the Plugin API reference for the full method list.