Skip to main content

Methods

The SDK exposes two imperative surfaces:

  • Swiftpatch — a tree-shakable namespace facade. Use this for event subscriptions and orchestrator verbs from anywhere in your app.
  • SwiftPatch — a class for environments with no React provider (headless JS, background tasks, native callbacks).

Most apps should stick with Swiftpatch plus hooks + provider. The SwiftPatch class is for advanced use cases.

The Swiftpatch facade

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

Event subscriptions

Every Swiftpatch.on*() call returns an unsubscribe function. Safe to call before the provider mounts — subscriptions are remembered.

const unsub1 = Swiftpatch.onUpdateAvailable((update) => {
console.log('Check found:', update.version);
});

const unsub2 = Swiftpatch.onUpdateReady((update) => {
console.log('Downloaded + ready:', update.version);
});

const unsub3 = Swiftpatch.onUpdateApplied((update) => {
console.log('Running the new bundle:', update.version);
});

const unsub4 = Swiftpatch.onUpdateDismissed((update) => {
console.log('User tapped Later on:', update.version);
});

const unsub5 = Swiftpatch.onError((error) => {
console.warn('Swiftpatch error:', error.code, error.message);
});

const unsub6 = Swiftpatch.onProgress(({ loaded, total }) => {
console.log(`${loaded} / ${total} bytes`);
});

const unsub7 = Swiftpatch.onRollback((reason) => {
console.warn('Rolled back:', reason);
});

// Wildcard — every event type
const unsub8 = Swiftpatch.on('*', (event) => {
console.log(event.type, event);
});

See Events for the full list of event types and payloads.

Orchestrator verbs

Once the provider is mounted (or new SwiftPatch().init() has completed), these are safe to call from anywhere.

// Apply the ready update + restart
await Swiftpatch.applyNow();

// Persist a per-release dismissal. A newer release will still surface.
await Swiftpatch.dismissUpdate();

// Snapshot of the current lifecycle state
const state = await Swiftpatch.getCurrentState();
// { phase: 'idle' | 'checking' | 'downloading' | 'ready' | 'applying' | 'error',
// current: BundleInfo | null,
// pending: UpdateInfo | null,
// lastCheckedAt: Date | null,
// lastError: SwiftPatchError | null }

// Persist a user-facing policy
await Swiftpatch.setUpdatePolicy({
prompt: 'mandatory-only', // 'always' | 'mandatory-only' | 'silent'
applyTiming: 'on-resume', // 'immediate' | 'on-resume' | 'on-next-launch'
});

const policy = await Swiftpatch.getUpdatePolicy();

// After a failed apply, retry once
await Swiftpatch.retryFailedUpdate();

// Check whether a given release should prompt (honors policy + dismissals)
const should = await Swiftpatch.shouldPromptFor(release);

// Check whether a release was previously dismissed
const dismissed = await Swiftpatch.isReleaseDismissed(release);

Channels

Channels let you ship to a subset of users (for example, beta) before promoting globally.

import { setChannel, getChannel, onChannelChanged } from '@swiftpatch/react-native';

// Set the active channel — persists to native storage.
await setChannel('beta');

// Read the current channel (hydrates from native on first call)
const current = await getChannel(); // 'production' | 'beta' | ...

// Subscribe to changes
const unsub = onChannelChanged((channel) => {
console.log('Channel is now:', channel);
});

Every checkForUpdate call passes the channel. Default is production. Custom channel names are allowed — configure them in the dashboard. See Channels guide.

Custom breadcrumbs

Forward Swiftpatch lifecycle events to your crash-reporting tool.

import { SwiftPatch } from '@swiftpatch/react-native';
const sp = new SwiftPatch({ deploymentKey: 'dep_xxx' });
await sp.init();

const unsub = sp.addBreadcrumbSink((crumb) => {
myTelemetry.log(crumb.message, {
level: crumb.level,
category: crumb.category,
data: crumb.data,
});
});

// Or emit your own crumb
sp.breadcrumb('info', 'user_tapped_apply', { releaseId: 'r_abc' });

One-liner adapters are shipped for the three popular vendors. See Integrations:

import { installSentrySink } from '@swiftpatch/react-native/integrations/sentry';
import * as Sentry from '@sentry/react-native';

installSentrySink(Sentry);

White-label — runtime tenant switching

Use one binary for many brands. Rotate the deployment key at runtime without restarting.

const sp = new SwiftPatch({ deploymentKey: initialKey });
await sp.init();

// Soft switch: next checkForUpdate uses the new tenant's release line.
await sp.setDeploymentKey(tenantKey);

// Hard switch: roll back to the store bundle and wipe all slots, so the user
// boots clean on the new tenant.
await sp.setDeploymentKey(tenantKey, { clearCurrentBundle: true });

// Read the persisted key
const key = sp.getDeploymentKey();

// Subscribe to changes — for example, re-theme the app after auth.
sp.onTenantChanged((key) => console.log('Tenant switched:', key));

See White-label guide.

The SwiftPatch class — non-React usage

For headless JS, background tasks, or native callbacks, construct a SwiftPatch instance directly:

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

const sp = new SwiftPatch({
deploymentKey: 'dep_xxx',
debug: __DEV__,
});

await sp.init();

// Check
const release = await sp.checkForUpdate();
if (release) {
// Download (uses delta-patch pipeline automatically when a patch is offered)
await sp.download(release);

// Install
await sp.install(release);

// Or do both in one call
// await sp.downloadAndInstall(release);

// Restart
sp.restart(release.id);
}

// Channels
await sp.setChannel('beta');
const channel = await sp.getChannel();

// Rollback to the previous bundle
await sp.rollback();

// Bundle info
const bundle = await sp.getCurrentBundle();

// Test the backend connection (no init required)
const ping = await sp.testConnection();
// { connected: true, app: {...}, organization: {...} }

Static helpers

import {
SwiftPatch,
getKnownErrors,
getErrorDescription,
testConnection,
getNativeConfig,
restart,
} from '@swiftpatch/react-native';

// Static — no init, no native call. Safe to call at module load.
const codes = SwiftPatch.getKnownErrors();
// ['SWIFTPATCH_DOWNLOAD_FAILED', 'SWIFTPATCH_HASH_MISMATCH', ...]

// Human-readable error copy
const info = getErrorDescription('SWIFTPATCH_SIGNATURE_INVALID');

// Quick connection check without full SDK init
const ping = await testConnection({ deploymentKey: 'dep_xxx' });

// Read native config
const cfg = await getNativeConfig();

// Force a JS reload (rarely needed — use applyNow instead)
restart();

Next steps