Skip to main content

Events

Swiftpatch emits a small set of strongly-typed events for every phase of the update lifecycle. The same events drive the built-in UI, the hooks, and any custom listeners you register.

Subscribing​

import { Swiftpatch } from '@swiftpatch/react-native';

const unsub = Swiftpatch.on('update-ready', (event) => {
console.log('Ready:', event.update.version);
});

// Later
unsub();

Every subscription returns an unsubscribe function. Listeners run in registration order. A throwing listener never breaks siblings — the bus catches and swallows exceptions so one bad consumer cannot brick your event pipeline.

The bus is SSR-safe. Subscriptions in non-React-Native environments (server rendering, unit tests) become no-ops with a noop unsubscribe.

The eight events​

type SwiftpatchEvent =
| { type: 'update-available'; update: UpdateInfo }
| { type: 'download-started'; hash: string; sizeBytes: number }
| { type: 'download-progress'; hash: string; loaded: number; total: number }
| { type: 'download-complete'; hash: string }
| { type: 'update-ready'; update: UpdateInfo }
| { type: 'update-applied'; update: UpdateInfo }
| { type: 'update-dismissed'; update: UpdateInfo }
| { type: 'rollback'; reason: string }
| { type: 'error'; error: SwiftPatchError };

update-available​

Fires once per release id when a checkForUpdate call finds a new release. Runs before the download starts.

Swiftpatch.onUpdateAvailable((update) => {
analytics.track('ota.update_available', { version: update.version });
});

download-started​

Fires when the download begins. Carries the target hash and the total size in bytes.

Swiftpatch.on('download-started', ({ hash, sizeBytes }) => {
console.log(`Starting ${sizeBytes} byte download for ${hash}`);
});

download-progress​

Fires periodically during the download. Use it to drive a progress bar.

Swiftpatch.onProgress(({ hash, loaded, total }) => {
const pct = total > 0 ? Math.round((loaded / total) * 100) : 0;
setProgress(pct);
});

download-complete​

Fires when bytes finish and the file passes SHA-256 verification. Always precedes update-ready.

update-ready​

Fires when a downloaded bundle has passed signature + hash verification and is sitting in the NEW slot, ready to apply.

Swiftpatch.onUpdateReady((update) => {
showToast(`Update ready: v${update.version}`);
});

update-applied​

Fires when applyNow() (or auto-apply) commits the update and triggers a restart. Note: because the process restarts, this event is observable from within the same JS session that called applyNow(), but not on the next session.

update-dismissed​

Fires when the user calls Swiftpatch.dismissUpdate() or taps Later on the built-in banner. The dismissal persists keyed on the release hash.

rollback​

Fires when the device rolls back to the previous stable slot. reason describes why:

ReasonMeaning
boot_loop_limitThe freshly applied bundle crashed too many times in a row.
server_rollbackThe server reports the applied release has been rolled back.
signature_invalidA downloaded bundle failed signature verification.
user_initiatedYour app called rollback() explicitly.

error​

Fires when any error surfaces in the SDK. The payload is a SwiftPatchError:

interface SwiftPatchError {
code: SwiftPatchErrorCode; // 'NETWORK_ERROR' | 'DOWNLOAD_ERROR' | ...
message: string;
details?: unknown;
}

For the full list of error codes, see Error codes.

Wildcard listener​

Subscribe to every event with '*':

Swiftpatch.on('*', (event) => {
telemetry.log('swiftpatch_event', event);
});

Event lifecycle diagram​

┌─────────────┐
│ check │──► update-available
└──────┬──────┘
│
▼
┌─────────────┐ download-started
│ download │──► download-progress (many)
└──────┬──────┘ download-complete
│
▼
┌─────────────┐
│ ready │──► update-ready
└──────┬──────┘
│
user taps Apply user taps Later
│ │
▼ ▼
update-applied update-dismissed
│
▼
restart()

At any point during download or apply, a failure triggers an error event. If apply fails repeatedly and the boot counter trips, a rollback event fires on the next cold start.

The useSwiftpatchUpdate hook​

Under the hood the hooks subscribe to these events and call setState. If you're building React UI, prefer the hook — you'll get re-renders for free. If you're building non-React UI or integrating with existing state (Redux, Zustand, MobX), subscribe via Swiftpatch.on(...).

Next steps​

  • Methods — imperative verbs that emit these events.
  • Hooks — React-friendly wrappers.
  • Integrations — pipe lifecycle events to Sentry / Bugsnag / Crashlytics.