Skip to main content

Quickstart

You will get from zero to a live OTA update in three commands. There is no AppDelegate editing, no Info.plist hand-patching, no pod install debugging. swiftpatch init does all of it for you.

Prerequisites:

  • React Native 0.76+ (Hermes recommended)
  • Node.js 20+
  • A Swiftpatch account (free)
  • CocoaPods for iOS builds

Step 1 — Install the SDK and CLI

# In your React Native project
npm install @swiftpatch/react-native

# Global CLI
npm install -g swiftpatch-cli

Then log in (opens a browser):

swiftpatch login

Step 2 — Wire everything up

npx swiftpatch init

This command does all of the setup for you:

  • Detects whether your project is bare React Native, Expo bare, or a monorepo.
  • Creates (or links) an app in your Swiftpatch dashboard.
  • Generates an Ed25519 signing keypair at .swiftpatch/keys/.
  • Patches AppDelegate.mm or AppDelegate.swift to load OTA bundles.
  • Patches MainApplication.kt or MainApplication.java to load OTA bundles on Android.
  • Adds SwiftPatchDeploymentKey to Info.plist and swiftpatch_deployment_key to strings.xml.
  • Registers com.swiftpatch.bg-refresh in BGTaskSchedulerPermittedIdentifiers and adds fetch + processing to UIBackgroundModes.
  • Writes a project config to .swiftpatchrc.
  • Adds .swiftpatch/, swiftpatch-artifacts/, and swiftpatch-private.pem to .gitignore.

Every patched file is wrapped in // swiftpatch-begin / // swiftpatch-end markers. The insertion is additive — your existing code is never rewritten, just wrapped with a block that calls Swiftpatch first and falls through to the default bundle loader if Swiftpatch returns nothing.

Safe to re-run

swiftpatch init is idempotent. If markers already exist, it skips the patch. If you want to remove everything cleanly, run swiftpatch unlink.

Manual install

If you prefer to copy-paste the snippets yourself, run swiftpatch init --manual. The CLI will still generate keys, write .swiftpatchrc, and update .gitignore — it just prints the native snippets for you to apply by hand.

Re-pod on iOS if you changed anything:

cd ios && pod install && cd ..

Step 3 — Wrap your app

App.tsx
import { SwiftPatchProvider } from '@swiftpatch/react-native';

export default function Root() {
return (
<SwiftPatchProvider config={{ debug: __DEV__ }}>
<App />
</SwiftPatchProvider>
);
}

You do not need to pass the deployment key — the SDK reads it from Info.plist / strings.xml that swiftpatch init wrote.

The provider ships with sensible defaults:

  • autoShowBanner (default true) — renders a polite <UpdateBanner /> when an update is ready.
  • blockOnMandatory (default true) — renders a full-screen <UpdateBlocker /> if the release is marked mandatory.
  • No further wiring required.

See SwiftPatchProvider reference for the full prop surface, or UI components if you want to theme the banner.

Step 4 — Ship your first update

Make any JS-only change (fix a typo, change a color, adjust a prop). Then:

swiftpatch deploy -p ios --hermes

This command:

  1. Runs react-native bundle with --reset-cache cleanly.
  2. Compiles to Hermes bytecode.
  3. Signs the bundle with your Ed25519 private key.
  4. Uploads to Cloudflare R2.
  5. Creates a release at app.swiftpatch.io.

Within seconds you will see the release appear in the dashboard. Relaunch your app — the update downloads in the background and the banner appears when it is ready to apply.

Android too

Run the same command with -p android. You can deploy both in parallel:

swiftpatch deploy -p ios --hermes & swiftpatch deploy -p android --hermes

What just happened

  • Your app cold-started and called the check-for-update API with the device's current bundle hash.
  • The server responded with a delta patch descriptor (a bsdiff file against your current bundle).
  • The SDK downloaded the patch (typically 60–98% smaller than the full bundle), verified the Ed25519 signature, applied bspatch on-device, and re-verified the resulting SHA-256.
  • The banner appeared. When you tapped "Apply now," the SDK swapped the active bundle slot and restarted the JS runtime.
  • Your app is now running the new code.

If anything at any step had failed — bad signature, hash mismatch, network error, bsdiff corruption — the SDK would have silently fallen back to a full download, then to the previous stable slot, then to the store-bundled JavaScript. Your users never see a crash.

Next steps