How it works
Swiftpatch is four components:
- CLI —
swiftpatch-cli. Bundles JS, signs it, uploads it, creates the release. - API —
api.swiftpatch.io. Accepts releases, generates delta patches, serves check-for-update, ingests telemetry, runs AI analysis. - CDN + storage — Cloudflare R2. Stores full bundles and delta patches.
- SDK —
@swiftpatch/react-native. Checks for updates, downloads and verifies bundles, applies them safely, reports telemetry.
The dashboard at app.swiftpatch.io sits alongside these, reading from the same API.
Architecture diagram
┌────────────────────┐ ┌──────────────────────────┐
│ Dev / CI machine │ │ User's device │
│ │ │ │
│ swiftpatch-cli │ │ @swiftpatch/ │
│ (bundle, sign, │ │ react-native (SDK) │
│ upload) │ │ │
└─────────┬──────────┘ └──────────┬───────────────┘
│ HTTPS │ HTTPS
│ │ (check / download)
▼ ▼
┌──────────────────────────────────────────────────┐
│ api.swiftpatch.io │
│ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │
│ │ Releases │ │ Delta-patch│ │ Crash │ │
│ │ service │ │ generator │ │ clustering + │ │
│ │ │ │ │ │ F8 PR agent │ │
│ └──────────┘ └────────────┘ └──────────────┘ │
└──────────────┬───────────────────┬───────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────┐
│ Cloudflare R2 │ │ Dashboard │
│ (bundles + │ │ app.swift │
│ patches + │ │ patch.io │
│ manifests) │ │ │
└─────────────────┘ └──────────────┘
Deploy lifecycle
You run swiftpatch deploy -p ios --hermes. The CLI:
- Runs
react-native bundlefor your entry file. - Compiles to Hermes bytecode (
--hermes). - Computes SHA-256 of the bundle + each asset.
- Builds a manifest (
manifest.json) pinning per-file hashes. - Zips everything into
bundle.zip. - Signs the archive hash with your Ed25519 private key.
- Uploads
bundle.zip+ signature to the API. - API pushes to R2, issues a signed CDN URL, stores the release in Postgres.
- API enqueues a delta-patch generation job — bsdiff against the last 5 releases.
- Delta patches land in R2 with their own signatures.
- CLI returns the release id.
Device lifecycle
Every cold start and foreground transition, the SDK:
The slot system
Three slots per device. See Brick protection for the full explainer:
- Binary — JS embedded in the IPA/APK. Always present. The floor.
- Stable — last OTA release that survived crash detection.
- New — most recent downloaded OTA release.
A downloaded release lands in New. After autoStabilizeAfterLaunches crash-free cold starts (default 2), it's promoted to Stable. A crash during that window rolls back.
Safety guarantees
Three independent layers that must all fail for a user to ever see a broken app:
- Signatures. Every bundle and patch is Ed25519-signed. Unsigned or tampered bytes never reach disk.
- Slot system. Old stable bundle is retained until the new one is verified.
- Boot counter + mount marker. If the new bundle crashes at launch, the SDK rolls it back within 2–3 cold starts — automatically, without needing a server round-trip.
See Brick protection.
Observability
Every phase emits events on the event bus (update-available, download-progress, update-ready, update-applied, rollback, error) and corresponding breadcrumbs for Sentry / Bugsnag / Crashlytics.
The backend ingests per-device telemetry (DOWNLOAD_PROD_STARTED, INSTALLED_PROD, ROLLBACK_PROD, PATCH_APPLIED_PROD, PATCH_FALLBACK_TO_FULL, CRASH_DETECTED, etc.). The dashboard graphs adoption, crash rate, and delta-patch savings per release.
AI features
When enabled for your org:
- Risk score per release, computed from bundle diff heuristics + crash model.
- Crash cluster analysis — root cause, suggested fix diff, confidence score.
- F8 PR agent —
swiftpatch pr <clusterId>opens a draft GitHub PR with the suggested fix.
See Crash detection and AI doctor.
Next steps
- Delta patches — bandwidth math and safety guarantees.
- Bundle signing — the Ed25519 security boundary.
- Brick protection — why a bad bundle never bricks your app.
- Rollouts — percent-based gradual rollouts.