npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@curvetechnologies/analytics-react-native

v0.3.1

Published

Curve mobile attribution and conversion tracking SDK for React Native

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.9

Run 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

  1. 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.
  2. 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.
  3. Preserve ct_handoff=ca... in the installed-app link. For Android install journeys, include the encoded handoff query in the Google Play URL's referrer parameter. URL-encode the inner query once when constructing that outer parameter.
  4. 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).
  5. Verify await Curve.getInstallAttribution() contains the expected handoff_id and permitted click context. A successful install upload adds install_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_open tracking defaults on. Set autoTrackAppOpen: false to manage lifecycle events yourself; resolving a handoff retains the existing behavior of recording first_open once.
  • The default queue holds 1,000 events; overflow drops the oldest. maxQueuedEvents supports 1–1,000 and maxBatchSize 1–50. Use a persistent storage adapter.
  • The default flush interval is 30 seconds. flushIntervalMs: 0 disables the timer; manual flushes and foreground transitions still attempt delivery. requestTimeoutMs defaults 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-After can 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.