Skip to main content
@traffical/react-native is the SDK for iOS and Android apps built with React Native. It handles the constraints mobile imposes — cold starts, offline use, app suspension, device context — and ships with sensible defaults so most apps don’t have to think about them.

Why mobile is different

Mobile has constraints web and backend don’t:
  • Cold start. On first launch there’s no cached bundle and no network response yet. The user sees the app before any policy can resolve.
  • Offline use. Users open the app on planes and in subways. Resolution must work without connectivity.
  • App store latency. Code changes take days. Parameter changes via Traffical are instant — this is the whole point of having a remote configuration system at all.
  • Background suspension. The app may be paused for hours and resumed. Cached assignments need refreshing.
Use evaluationMode: "bundle" for mobile. The SDK fetches the config bundle once and resolves on-device, so every subsequent decision is local — no per-decision network call, and it keeps working offline. This is also the only mode that works with a publishable key, which is the key type that belongs in a mobile binary. The package’s own default is still evaluationMode: "server", where each resolution is evaluated for that call’s context on the edge and the response is persisted to AsyncStorage across launches. Because getParams/decide are synchronous, a call returns the last-good cached response and converges as fresh resolves land. Server mode requires a server-side SDK key, so on mobile it means proxying through your own backend.

Installation

For iOS, run pod install in the ios/ directory after adding @react-native-async-storage/async-storage.

Setup

Use a publishable SDK key (traffical_pk_...) — it is safe to ship in a mobile binary. The SDK auto-registers the AsyncStorage cache and lifecycle hooks for foreground/background transitions.
Set evaluationMode: "bundle" with a publishable key.This SDK defaults to evaluationMode: "server", which resolves through /v1/resolve. That endpoint accepts only server-side SDK keys, so leaving the default in place with a traffical_pk_... key returns 403 on every resolution.Bundle mode fetches the config bundle once and resolves on-device — no per-decision network call, and it works offline. It is the recommended mode for mobile regardless of key type.Server-evaluated mode needs a server-side SDK key (traffical_sk_...), which must never ship in a mobile binary. If you need it, proxy resolution through your own backend.

Resolving parameters

Same hook as the React SDK:

Cold start strategy

Resolution checks three sources in order: First launch, no localConfig: the user sees defaults until the first network call returns. If your first-launch onboarding is a critical test, embed a localConfig bundle built during CI:
Returning user: the SDK reads cached assignments from AsyncStorage synchronously before the first render. The user immediately sees their previous-session assignment. A background refresh updates for next time. Returning user after long absence (cache expired): falls back to localConfig, then to caller defaults.

Device-info enrichment

The SDK can enrich context with device info — useful for OS-specific targeting and analytics. You supply the data through a deviceInfoProvider: any object implementing the DeviceInfoProvider interface. The returned fields merge into context on every resolution, so they’re available for policy conditions.

The default provider

defaultDeviceInfoProvider derives device metadata from React Native’s Platform and Dimensions plus Intl — no native modules. It is opt-in: nothing is added to your context unless you pass it.
It emits the $-prefixed system attributes shared by every Traffical SDK — registered in every project, so you can target them straight from the condition editor — plus un-prefixed fields for compatibility with existing conditions: Values are re-read on every call, so rotation and locale changes are picked up on the next decision. To populate $app_version, build the provider with the version from your own source (expo-constants, react-native-device-info, or a generated constant):

Your own provider

Implement DeviceInfoProvider yourself to add fields the default cannot read, typically backed by react-native-device-info:
Use the $ keys in new conditions — e.g. $os eq "ios" for an iOS-only policy.
Version-string targeting is a known gap. The numeric comparison operators (gt/gte/lt/lte) only compare numbers — a condition like appVersion gte "2.0.0" won’t match, because "2.0.0" is a string. To gate on a minimum version today, target an exact set of versions with in, or expose a numeric build number (appBuildNumber) and compare that. Semver-aware version conditions are not yet supported.

Tracking events

Events are batched in memory and flushed on background or app close.

Foreground refresh

When the app returns to the foreground after being suspended, the SDK checks whether the cache is stale and refreshes silently if so. Existing assignments stay stable — the refresh only matters for new parameters or next-session resolutions.

Options

Persistence is backed by AsyncStorage automatically — the provider registers it for you, so there’s no storage option to set. The provider also accepts the common options shared with the other SDKs (trackDecisions, exposureSessionTtlMs, eventBatchSize, eventFlushIntervalMs, disableCloudEvents, assignmentLogger, unitKeyFn, contextFn, plugins).

Next steps

Mobile app pattern

Onboarding and in-app tests.

How it works

Evaluation modes and resolution.