React Native SDK
@healkite/react-native supports React Native 0.80+ with the New Architecture and Hermes, on iOS (Swift) and Android (Kotlin).
Install
sh
npm install @healkite/react-native && cd ios && pod installUsing Expo? Follow Expo instead of the native steps below.
Configure
iOS Info.plist | Android res/values/strings.xml | |
|---|---|---|
| CDN base URL | HealkiteCdnUrl | healkite_cdn_url |
| Channel key | HealkiteChannel | healkite_channel |
| Runtime version (default: app version) | HealkiteRuntimeVersion | healkite_runtime_version |
| Your bundle-signing public key | HealkitePublicKey | healkite_public_key |
| Server manifest key | HealkiteManifestKey | healkite_manifest_key |
| Backup CDN (optional) | HealkiteCdnFallbackUrl | healkite_cdn_fallback_url |
The dashboard's Settings tab shows these values ready to copy. On Healkite Cloud the CDN URL is
https://cdn.healkite.com. Use HTTPS in release builds.
Load the OTA bundle
iOS, in your RCTDefaultReactNativeFactoryDelegate (for example AppDelegate.swift):
swift
import Healkite
override func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
HealkiteCore.bundleURL() ?? Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}Android, in MainApplication.kt:
kotlin
import dev.healkite.Healkite
getDefaultReactHost(
context = applicationContext,
packageList = PackageList(this).packages,
jsBundleFilePath = if (BuildConfig.DEBUG) null else Healkite.getJSBundleFile(this),
)Sync
tsx
import { useEffect } from "react";
import Healkite, { KillSwitchGate } from "@healkite/react-native";
export default function App() {
useEffect(() => {
Healkite.sync(); // confirm this bundle, check, install, send telemetry. Never throws.
}, []);
return <KillSwitchGate>{/* your app */}</KillSwitchGate>;
}Call sync() (or notifyAppReady()) after your first screen renders. A new bundle that never gets there
is rolled back on the next launch and never retried.
API
sync({ installMode? }) | Ready → check → install → restart if mandatory or installMode: "immediate" → telemetry |
checkForUpdate() | UPDATE_AVAILABLE, UP_TO_DATE, NOT_IN_ROLLOUT, SKIPPED_FAILED, ROLLBACK_TO_EMBEDDED, NO_RELEASE |
notifyAppReady() | Confirm the running bundle |
restart() | Apply a staged update now |
getState() | Running, pending and failed bundles, last rollback, last install method |
trackEvent(name, props?) | Business events used by guarded rollouts and experiments |
installErrorHandler() | Report fatal JS errors (crash-free sessions) |
getKillSwitch(), useKillSwitch(), <KillSwitchGate fallback?> | Maintenance mode |
openPreview(url), exitPreview(), <PreviewBanner /> | PR previews |
getPushTopic(), handlePush(data) | Instant hotfix |
flushEvents() | Send queued telemetry now |
Crashes and business metrics
ts
Healkite.installErrorHandler(); // once, at startup
Healkite.trackEvent("checkout_success"); // whenever it happensGuarded rollouts compare these per session with the previous release.
Testing
js
jest.mock("@healkite/react-native", () => require("@healkite/react-native/jest/mock"));How an update is applied
- The manifest is fetched (CDN-cached, revalidated) and its server signature verified.
- The device decides rollout membership locally from a hash of its random id.
- The bundle signature is checked before download. A patch is used when one exists for the running bundle.
- Every file is checked against its sha256 and unpacked into the app's support directory.
- The next launch is a trial;
notifyAppReady()keeps it. A crash before that rolls back. - A new store binary always starts from its embedded bundle.