configure(config)
Apply runtime configuration. Call once before any other SDK call. Subsequent calls replace the config and reset the analytics emitter, but the recommended pattern is one call at app startup.
The SDK can be configured from JS, from Swift, or from Kotlin — all three end up calling the same singleton (NitroPushSdk.shared). Pick the runtime closest to where the rest of your app’s bootstrap config already lives.
From JS the API has two flavours.
configureWith({...})takes an explicit config and returns aNitroPushClientyou call every other method on.configure()is the no-arg variant — it readsNITROPUSH_*keys fromInfo.plist(iOS) /AndroidManifest.xml<meta-data>(Android). Native code has the symmetric helpersNitroPushSdk.configFromInfoPlist()/NitroPushSdk.configFromManifest().
Signature
The shape is identical across runtimes — serverUrl, deploymentKey, storageBaseUrl, optional appVersion + clientUniqueId.
Native Swift type. Used by NitroPushSdk.shared.configure() in Swift.
public struct NPConfig {
public let serverUrl: String
public let deploymentKey: String
/// Public root of the storage bucket. Joined with each
/// `objectKey` in the SDK manifest at fetch time.
public let storageBaseUrl: String
/// Falls back to CFBundleShortVersionString if nil.
public let appVersion: String?
/// Falls back to identifierForVendor if nil.
public let clientUniqueId: String?
} Native Kotlin data class. Used by NitroPushSdk.shared.configure() in Kotlin.
data class NlConfig(
val serverUrl: String,
val deploymentKey: String,
/** Public root of the storage bucket. Joined with each
* `objectKey` in the SDK manifest at fetch time. */
val storageBaseUrl: String,
/** Falls back to BuildConfig.VERSION_NAME if null. */
val appVersion: String? = null,
/** Falls back to a persisted UUID if null. */
val clientUniqueId: String? = null,
) TypeScript interface for the JS configureWith() helper. The no-arg configure() takes no argument and reads the same keys from Info.plist / AndroidManifest meta-data.
// Explicit config — returns a client you call every other method on.
configureWith(config: NitroPushConfig): NitroPushClient;
// No-arg — reads NITROPUSH_* from Info.plist (iOS) /
// AndroidManifest <meta-data> (Android). Same return type.
configure(): NitroPushClient;
interface NitroPushConfig {
/** NitroPush API base URL. */
serverUrl: string;
/** The environment's deployment key. */
deploymentKey: string;
/**
* Public root of the storage bucket your CLI uploads to.
* Joined with each `objectKey` in the SDK manifest at fetch time.
*/
storageBaseUrl: string;
/**
* Native binary version this build targets. Falls back to the
* binary's CFBundleShortVersionString (iOS) / versionName (Android)
* if omitted.
*/
appVersion?: string;
/**
* Stable per-device id for deterministic rollout bucketing.
* Falls back to identifierForVendor (iOS) / persisted UUID
* (Android). Recommended: leave unset.
*/
clientUniqueId?: string;
} Example
Call from AppDelegate.didFinishLaunchingWithOptions, before React Native loads.
import NitroPush
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
try? NitroPushSdk.shared.configure(
NPConfig(
serverUrl: "https://app.nitropush.org",
deploymentKey: ProcessInfo.processInfo.environment["NL_KEY"] ?? "",
storageBaseUrl: "https://your-bucket.s3.amazonaws.com"
)
)
// Optional: turn on the SDK's debug log stream while you're wiring this up.
// NitroPushSdk.shared.setEnableLogs(true)
return true
} Call from MainApplication.onCreate, immediately after NitroPushSdk.install(this).
import com.nitropush.sdk.NitroPushSdk
import com.nitropush.sdk.NlConfig
override fun onCreate() {
super.onCreate()
NitroPushSdk.install(this) // creates the singleton
NitroPushSdk.shared.configure(
NlConfig(
serverUrl = "https://app.nitropush.org",
deploymentKey = BuildConfig.NL_KEY,
storageBaseUrl = "https://your-bucket.s3.amazonaws.com"
)
)
// Optional: turn on the SDK's debug log stream (tagged "NitroPush") while
// you're wiring this up.
// NitroPushSdk.shared.setEnableLogs(true)
} Call once at module load, at the very top of your app entry. Capture the returned client and use it for every subsequent call.
import { configureWith, type NitroPushClient } from '@nitropush/react-native';
// Build the client once at module scope, then use it everywhere.
export 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!,
});
// Alternative — if you've put the same values in Info.plist (iOS) /
// AndroidManifest <meta-data> (Android), no JS-side config needed:
//
// import { configure } from '@nitropush/react-native';
// export const client = configure(); That’s all you need for 99% of apps. Everything else has a sensible default.
What happens after configure()
In order:
- The native side stores the config + resolves any fallbacks (
appVersion,clientUniqueId). - The native analytics emitter gets created (or replaced) and starts a 30-second flush timer.
- An
app_startedanalytics event fires immediately. - The SDK sweeps the launch-time rollback state — if the previous launch activated a bundle that never reached
notifyAppReady(), aninstall_failed_rollbackevent fires for that release.
After this, the SDK is idle until you call sync(), notifyAppReady(), or any of the lifecycle methods.
Configuring from native AND JS. They share the singleton. The most recent
configure()wins for any subsequent call. Configuring from native gives you a head start (the SDK is ready before JS boots) — useful for background updates or a pre-JS download.
Per-environment config
Different builds (test / stage / prod) target different environments, which means different deployment keys. Wire env vars at build time and let configure() read them:
Read from your Info.plist or an Xcode build setting injected at compile time. Or use configFromInfoPlist() to read NITROPUSH_* keys directly.
// Option A: have the SDK read NITROPUSH_* keys from Info.plist itself.
try NitroPushSdk.shared.configure(NitroPushSdk.configFromInfoPlist())
// Option B: read whatever keys you've already injected (e.g. a per-target
// xcconfig) and build the NPConfig yourself.
let key = Bundle.main.object(forInfoDictionaryKey: "NITROPUSH_DEPLOYMENT_KEY") as? String
?? ""
try NitroPushSdk.shared.configure(
NPConfig(
serverUrl: "https://app.nitropush.org",
deploymentKey: key,
storageBaseUrl: "https://your-bucket.s3.amazonaws.com"
)
) Inject via BuildConfig fields from Gradle product flavors. Or put NITROPUSH_* under <application> <meta-data> in AndroidManifest.xml and use configFromManifest().
// Option A: have the SDK read NITROPUSH_* keys from AndroidManifest meta-data.
NitroPushSdk.shared.configure(NitroPushSdk.configFromManifest())
// Option B: read whatever values you've injected via Gradle and build NlConfig.
// In app/build.gradle.kts:
// buildConfigField("String", "NL_DEPLOYMENT_KEY", "\"PROD-KEY-…\"")
NitroPushSdk.shared.configure(
NlConfig(
serverUrl = "https://app.nitropush.org",
deploymentKey = BuildConfig.NL_DEPLOYMENT_KEY,
storageBaseUrl = "https://your-bucket.s3.amazonaws.com"
)
) EXPO_PUBLIC_* are inlined at build time. For bare RN, use react-native-config.
// Explicit config from env (the most common pattern).
export const client = configureWith({
serverUrl: process.env.NITROPUSH_SERVER_URL!,
deploymentKey: process.env.NITROPUSH_DEPLOYMENT_KEY!,
storageBaseUrl: process.env.NITROPUSH_STORAGE_BASE_URL!,
});
// Or — when the same values are already in Info.plist / AndroidManifest:
// export const client = configure(); Common mistakes
Calling configure() from a deferred path. It must run before any sync() / client.notifyAppReady() / etc. The SDK will throw “not configured” if you try to use it without configuring first. Keep configureWith() / configure() at the top of your app entry — never inside a useEffect.
Using a different clientUniqueId per launch. If you override clientUniqueId, make sure it’s stable across launches — otherwise deterministic rollout bucketing breaks (a device on the 25% bucket would flip to a new bucket every launch). The native fallback is stable; prefer it.
Forgetting storageBaseUrl. If your storage bucket has a custom CDN or a path prefix, storageBaseUrl must include it. Open the Storage admin page — the value is shown once a provider is configured. With MinIO the API runs on a different port than the Console (typically :9000 vs :9001) — use the API port.
Next: sync() →