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:
| Reason | Meaning |
|---|---|
boot_loop_limit | The freshly applied bundle crashed too many times in a row. |
server_rollback | The server reports the applied release has been rolled back. |
signature_invalid | A downloaded bundle failed signature verification. |
user_initiated | Your 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.