Nitro SDK

@nitropush/react-native is the device-side library that ships in your React Native or Expo app. The whole core is plain Swift + Kotlin — the JS surface (via Nitro Modules) is a thin wrapper. That means every operation works in three runtimes:

  • From JSconfigureWith({...}) returns a NitroPushClient; you call client.checkForUpdate() / client.notifyAppReady() / etc. on the returned client. sync(client, ...) is a one-call helper that wraps check → download → install.
  • From SwiftNitroPushSdk.shared exposed as a public final class. Use it for pre-JS downloads, background fetches via BGTaskScheduler, or recovering from a broken JS bundle.
  • From KotlinNitroPushSdk.shared (after NitroPushSdk.install(this) in your MainApplication). Same role: background updates via WorkManager, native splash-screen update flows, etc.

What it does

  • Update checks — polls NitroPush for new bundles.
  • Downloads — streams bundle + assets straight from your storage bucket, verifies SHA-256.
  • Installs — flips the JS bundle pointer at the moment you choose (immediate / next restart / next resume / next suspend).
  • Rollback safety net — if notifyAppReady() doesn’t run on a fresh install, the next launch falls back to the previous bundle.
  • Native analytics — download / install / rollback events fire from Swift + Kotlin, never the JS thread, so you see the truth even when a bundle crashes on cold start.
  • Toggleable debug logsNitroPushSdk.shared.setEnableLogs(true) from native turns on per-action traces (tagged NitroPush) for the whole SDK lifecycle.

The public surface

The Swift singleton. ~14 public methods on NitroPushSdk.shared.

import NitroPush

NitroPushSdk.shared.configure(_:)               // or configFromInfoPlist()
NitroPushSdk.shared.setEnableLogs(_:)           // debug log toggle
NitroPushSdk.shared.checkForUpdate()            // async
NitroPushSdk.shared.downloadUpdate(_:)          // async
NitroPushSdk.shared.installUpdate(_:_:_:)       // async
NitroPushSdk.shared.notifyAppReady()
NitroPushSdk.shared.restartApp(onlyIfUpdateIsPending:)
NitroPushSdk.shared.rollback(releaseId:)        // throw on no-previous
NitroPushSdk.shared.getCurrentPackage()
NitroPushSdk.shared.getPendingPackage()
NitroPushSdk.shared.clearPendingUpdate()
NitroPushSdk.shared.clearUpdates()
NitroPushSdk.shared.activeBundleURL()           // for AppDelegate.bundleURL()
NitroPushSdk.shared.addDownloadProgressListener(_:)

The Kotlin singleton. Same shape — call install() from MainApplication first.

import com.nitropush.sdk.NitroPushSdk

NitroPushSdk.install(application)               // once, from MainApplication
NitroPushSdk.shared.configure(config)           // or configFromManifest()
NitroPushSdk.shared.setEnableLogs(true)         // debug log toggle
NitroPushSdk.shared.checkForUpdate()            // BLOCKING
NitroPushSdk.shared.downloadUpdate(pkg)         // BLOCKING
NitroPushSdk.shared.installUpdate(pkg, mode, minimumBgSeconds)
NitroPushSdk.shared.notifyAppReady()
NitroPushSdk.shared.restartApp(onlyIfUpdateIsPending)
NitroPushSdk.shared.rollback(releaseId)
NitroPushSdk.shared.getCurrentPackage()
NitroPushSdk.shared.getPendingPackage()
NitroPushSdk.shared.clearPendingUpdate()
NitroPushSdk.shared.clearUpdates()
NitroPushSdk.shared.activeBundleFile()          // for ReactNativeHost
NitroPushSdk.shared.addDownloadProgressListener { … }

Top-level exports from @nitropush/react-native. configureWith() returns a NitroPushClient — every runtime method lives on the client, not as a free function.

import {
  // 3 top-level functions
  configure,        // no-arg, reads Info.plist / AndroidManifest meta-data
  configureWith,    // explicit config
  sync,             // sync(client, options?, onStatus?, onProgress?)

  // enums
  InstallMode,
  SyncStatus,

  // types
  type NitroPushClient,
  type NitroPushConfig,
  type LocalPackage,
  type RemotePackage,
  type DownloadProgress,
  type SyncOptions,
} from '@nitropush/react-native';

// Everything else is on the client returned from configure/configureWith:
//   client.checkForUpdate(deploymentKeyOverride?)
//   client.notifyAppReady()
//   client.restartApp(onlyIfUpdateIsPending)
//   client.getCurrentPackage()
//   client.getUpdateMetadataSync()
//   client.getPendingPackage()
//   client.clearUpdates()
// …plus methods on the returned packages:
//   remote.download(onProgress)
//   local.install(installMode, minimumBackgroundDuration)
//   local.rollback()

How to read these docs

  • Installation — npm + native wiring (~5 min).
  • configure() — required runtime config, in any of the three runtimes.
  • sync() — the main update loop. JS one-call helper + the three native steps that compose into it.
  • LifecyclenotifyAppReady, restartApp, rollback, the rollback safety net, setEnableLogs.

Quick example

The shortest path to “my app self-updates.” Pick the runtime closest to where the rest of your bootstrap code already lives.

Configure + sync from AppDelegate — runs before React Native loads, perfect for pre-JS update checks.

import UIKit
import NitroPush

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(
    _ app: UIApplication,
    didFinishLaunchingWithOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    try? NitroPushSdk.shared.configure(
      NPConfig(
        serverUrl:      "https://app.nitropush.org",
        deploymentKey:  "PROD-KEY-…",
        storageBaseUrl: "https://your-bucket.s3.amazonaws.com"
      )
    )
    // Fire and forget — checks for an update during launch animation.
    Task {
      if let remote = try? await NitroPushSdk.shared.checkForUpdate() {
        let local = try? await NitroPushSdk.shared.downloadUpdate(remote)
        if let local {
          try? await NitroPushSdk.shared.installUpdate(
            pkg: local,
            installMode: .onNextRestart,
            minimumBackgroundDuration: 0
          )
        }
      }
    }
    return true
  }
}

Configure + sync from MainApplication — runs before React Native loads.

import android.app.Application
import com.nitropush.sdk.NitroPushSdk
import com.nitropush.sdk.NlConfig
import com.nitropush.sdk.NlInstallMode
import kotlinx.coroutines.*

class MainApplication : Application() {
  override fun onCreate() {
    super.onCreate()
    NitroPushSdk.install(this)
    NitroPushSdk.shared.configure(
      NlConfig(
        serverUrl      = "https://app.nitropush.org",
        deploymentKey  = BuildConfig.NL_KEY,
        storageBaseUrl = "https://your-bucket.s3.amazonaws.com"
      )
    )
    CoroutineScope(Dispatchers.IO).launch {
      runCatching {
        val remote = NitroPushSdk.shared.checkForUpdate() ?: return@runCatching
        val local = NitroPushSdk.shared.downloadUpdate(remote)
        NitroPushSdk.shared.installUpdate(
          pkg = local,
          installMode = NlInstallMode.ON_NEXT_RESTART,
          minimumBackgroundDurationSeconds = 0.0
        )
      }
    }
  }
}

configureWith() at module-load → returned client used everywhere. sync(client, ...) from a top-level useEffect.

import { useEffect } from 'react';
import {
  configureWith,
  sync,
  InstallMode,
  type NitroPushClient,
} from '@nitropush/react-native';

// One-time, at JS startup. Reads env vars set at build time. The
// returned client owns every runtime method.
const client: NitroPushClient = configureWith({
  serverUrl:       process.env.EXPO_PUBLIC_NITROPUSH_SERVER_URL!,
  deploymentKey:   process.env.EXPO_PUBLIC_NITROPUSH_DEPLOYMENT_KEY!,
  storageBaseUrl:  process.env.EXPO_PUBLIC_NITROPUSH_STORAGE_BASE_URL!,
});

export function App() {
  useEffect(() => {
    // Confirms a healthy boot. Without this, fresh installs roll back.
    client.notifyAppReady();
    // Background sync — gentle default. `sync` takes the client as its
    // first arg, then options / status / progress callbacks.
    sync(client, { installMode: InstallMode.ON_NEXT_RESTART }).catch(() => {});
  }, []);

  return /* your app */;
}

What gets sent on the wire

For every release event the SDK observes, it posts a small analytics blob to the platform — release.created, download_started, download_completed, install_completed, install_failed_rollback. This is what powers the live release counts in the dashboard and what feeds the Slack/Discord notifications you set up in Get started → Notifications.

The events fire from native code on a background thread (regardless of whether you triggered the operation from JS or native), queue locally if the network’s down, and retry with exponential backoff. There’s no opt-out today — analytics is core to how the rollout / rollback flows work.


Next: Installation →