@curvetechnologies/analytics-react-native
v0.3.1
Published
Curve mobile attribution and conversion tracking SDK for React Native
Maintainers
Readme
Curve React Native SDK
@curvetechnologies/analytics-react-native sends app events to Curve, preserves install attribution, and retries offline events. Curve's backend separately controls destination mappings, consent enforcement, and delivery to Meta and other advertising platforms.
Install and initialize
Register your Android or iOS app in Curve → Mobile Apps. Obtain its app ID, the organization tracking API key, and the ingestion URL. The registered Android package name or iOS bundle ID must match the native app. Use a separate Curve app registration for each platform.
For a React Native Community CLI app, add the native modules as direct dependencies of the host app, then rebuild:
npm install @curvetechnologies/[email protected] \
@react-native-async-storage/async-storage@^1.23.1 \
react-native-play-install-referrer@^1.1.9Run your normal CocoaPods installation for iOS, then produce a fresh Android/iOS binary. A JavaScript update cannot add native modules to an existing binary. AsyncStorage preserves identity, consent, attribution, and the queue. Play Install Referrer supplies Android install context; it is never queried on iOS. Verify both modules in the host's native dependency configuration and build output. The SDK currently depends on AsyncStorage 1.x; a host using another major can install a second JavaScript copy. Align versions or supply a tested storage adapter and verify the resulting native dependency graph.
React Native CLI autolinking examines host dependencies. SDK transitive dependencies alone are insufficient for that workflow. Expo uses its own autolinking rules; use a native development/production build containing these modules and compatible dependency versions. Expo Go is insufficient for the custom Play module.
Initialize once and await completion before calling other SDK methods. Use one active client per storage namespace.
import { Curve } from '@curvetechnologies/analytics-react-native';
// Start restricted. Only the host app knows the platform permission state.
Curve.setDeviceContext({ limitAdTracking: true });
await Curve.init({
apiKey: 'YOUR_ORGANIZATION_TRACKING_KEY',
appId: 'YOUR_ANDROID_CURVE_APP_ID',
endpointBaseUrl: 'YOUR_CURVE_INGESTION_URL',
platform: 'android',
packageName: 'YOUR_REGISTERED_ANDROID_PACKAGE',
appVersion: 'YOUR_APP_VERSION',
defaultConsent: {
analyticsAllowed: false,
advertisingAllowed: false,
consentGiven: false,
},
});For iOS, supply platform: 'ios', the iOS registration's appId, and bundleId instead of packageName. Native adapters load automatically in React Native. Custom adapters override individual ports; unspecified ports retain native defaults.
The example intentionally waits for host consent. If defaultConsent.analyticsAllowed is omitted, the legacy default remains analytics enabled and advertising disabled. An explicit analyticsAllowed: false suppresses collection even when consentGiven is false. Choose defaults appropriate to your actual consent flow.
Missing native storage causes a warning and a memory fallback: identity and queued events then do not survive app restarts. Treat that warning as an integration failure before launching campaigns. A missing or failing Play module also warns. A silent default Play callback times out after 10 seconds so initialization can continue; this does not prove install attribution was obtained.
Consent and device permissions
After your consent flow, pass its real result:
await Curve.setConsent({
analyticsAllowed: analyticsWasGranted,
advertisingAllowed: advertisingWasGranted,
consentGiven: true,
// Include only a real token supplied by your Curve consent integration.
// consentToken: token,
});
// iOS: true until ATT is authorized. Android: reflect the applicable device
// tracking restriction and your app's permission policy.
Curve.setDeviceContext({ limitAdTracking: !deviceTrackingWasAuthorized });Unknown device permission is sent as restricted (limit_ad_tracking: true) in 0.3.1. Only an explicit host false enables the device tracking flag. This does not grant advertising consent. The SDK does not display ATT prompts, collect IDFA/GAID, or implement AdAttributionKit/SKAdNetwork.
Withdrawing analytics consent discards queued events, pending link retries, and pending attribution uploads, and suppresses new events and subsequent sends. Existing local install attribution remains available; withdrawal is not a data-erasure API. Events already submitted to the network cannot be recalled. Granting consent later does not revive discarded events. If automatic lifecycle tracking is enabled, an initial denial does not consume first_open; the first later grant can enqueue it once.
setConsent persists the SDK's consent state and supplies the ingestion envelope. It does not create the backend CMP consent record or certify destination enforcement. Validate the backend consent integration separately.
Connect a campaign click to the app
- Configure and verify your app's Android App Links or iOS Universal Links and the allowed link hosts in Curve. The SDK does not configure OS link routing.
- On the website, create a Curve app handoff token with the actual permitted click identifiers, browser cookies, UTMs, and consent context. Use the registered destination app and host. Do not invent click IDs or Meta cookies.
- Preserve
ct_handoff=ca...in the installed-app link. For Android install journeys, include the encoded handoff query in the Google Play URL'sreferrerparameter. URL-encode the inner query once when constructing that outer parameter. - The SDK reads the Android Play referrer at startup and resolves the token for the persistent app instance. It also listens for initial and subsequent app links. If your navigation integration consumes links itself, pass the original URL to
await Curve.resolveHandoff(url). - Verify
await Curve.getInstallAttribution()contains the expectedhandoff_idand permitted click context. A successful install upload addsinstall_attribution_id.
The SDK fingerprints complete deep links for deduplication; it never persists raw handoff tokens or full link URLs. Normal navigation links leave campaign attribution intact. A successfully resolved newer link supersedes older unresolved links. The latest stored context is used for unsent events; already acknowledged events are not retroactively changed.
Transient resolver errors remain retryable. Known unresolved handoffs delay event delivery until resolution, a definitive failure, or the in-memory link retry deadline (seven days). Live links received while analytics is denied are ignored; an unread initial link is deferred until consent allows processing. Link retries run during flushes and remain in memory, bounded to 16 URLs. They cannot survive process death unless the OS/host supplies the URL again. Play referrers can be read again after restart; the processed flag is set after durable handling, and transient token failures do not consume it. Google's referrer retention and your Curve token's expiry are separate limits.
Sanitized install attribution is saved locally before its upload. Pending uploads survive restarts and retry with bounded backoff. Raw referrer queries are not forwarded; only parsed attribution and timestamps are sent. A manual integration can call:
await Curve.recordInstallAttribution({
raw: actualPlayReferrer,
clickTimestampMs: actualClickTimestampMs,
installBeginTimestampMs: actualInstallTimestampMs,
});An unavailable native referrer or an iOS App Store journey without a recoverable link cannot establish deterministic attribution. This SDK does not implement deferred deep linking through the iOS App Store.
Send conversion events
const clientEventId = await Curve.track('subscription_started', {
value: 49.99,
currency: 'USD',
order_id: 'NON_IDENTIFYING_ORDER_ID',
});
await Curve.flush();Track a real business transition once. Calling track twice creates two distinct events; transport deduplication does not deduplicate duplicate business instrumentation. null means analytics consent suppressed the call. A returned ID means the event was queued locally, not that Meta received or attributed it.
Use value and currency for monetary values; amount aliases to value, with an explicit value taking precedence. The property filter is a fixed allowlist plus limited custom_ keys. It removes some obvious identifying keys and values but does not understand clinical meaning. Do not send patient information, treatment details, diagnoses, free text, or condition-revealing names in events, properties, identifiers, URLs, or campaigns. A custom_ prefix does not make sensitive content safe.
For server-generated orders, preserve app_instance_id from await Curve.getAppInstanceId() in your authorized order flow. The server must supply/join the required attribution and device context. This SDK does not repair missing joins in a server-to-server order endpoint. Avoid sending the same conversion through independent app and server paths without a deliberate business deduplication design.
Confirm destination delivery before spending
Mobile events use Curve's mobile ingestion/mapping path. Native apps have no browser DOM for automatic click/form selectors. Instrument app lifecycle and business outcomes explicitly. Website DOM events and mobile events have different collection and attribution paths.
Configure the registered app's connector, destination policy, and the required backend forwarding gates. Curve's Meta policy uses organization-wide sequential aliases event_1, event_2, … with custom_only and standard_event_name: null. Do not create Meta Custom Conversions or send descriptive SDK names as destination events. An installation with older mobile_eventN mappings needs a backend/dashboard correction; changing the SDK event string is insufficient.
Verify these stages separately with an authorized test app/dataset: app instance and click context → Curve event receipt → mobile mapping and consent outcome → successful Meta delivery → event visibility in Events Manager → eligibility for the intended campaign objective and optimization event in the actual ad account. Receipt by Curve is not proof of Meta delivery, and Meta receipt is not proof of attributed or optimizable conversions. This SDK release alone does not certify full Meta App Promotion attribution.
Delivery behavior and configuration
- Automatic
first_open/app_opentracking defaults on. SetautoTrackAppOpen: falseto manage lifecycle events yourself; resolving a handoff retains the existing behavior of recordingfirst_openonce. - The default queue holds 1,000 events; overflow drops the oldest.
maxQueuedEventssupports 1–1,000 andmaxBatchSize1–50. Use a persistent storage adapter. - The default flush interval is 30 seconds.
flushIntervalMs: 0disables the timer; manual flushes and foreground transitions still attempt delivery.requestTimeoutMsdefaults to 10 seconds for network adapters. - Retries preserve the event ID and serialized event item. Only acknowledged event IDs are removed. Malformed, incomplete, omitted, or in-progress acknowledgements remain queued. A terminal ingress guard response discards the batch without implying destination delivery.
- Retry backoff is exponential up to roughly 72 seconds including jitter. A valid
Retry-Aftercan extend it to a maximum of 24 hours. Authentication/app-configuration errors (401/403/404) delay retries by at least one hour so operators can repair configuration without losing queued events. Invalid install-upload payloads (400/413/422) stop retrying while retaining local attribution. Invalid event batches are isolated so one rejected event does not discard its siblings. await Curve.shutdown()removes lifecycle hooks and waits for pending SDK storage/queue work. Await shutdown before reinitializing. Custom ports must settle their operations and implement their own native cancellation where applicable.
Use a dedicated test environment for development. The repository's browser harness uses mocked endpoints; its tests do not send production conversions or establish actual Meta campaign eligibility.
