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

@appsynk/react-native-sdk

v2.0.3

Published

AppSynk Mobile Attribution SDK for React Native

Readme

@appsynk/react-native-sdk

Official React Native SDK for AppSynk — the Mobile Attribution Platform.

This package is a thin bridge over the native AppSynk SDKs (the AppSynk CocoaPod on iOS, io.appsynk:sdk on Android). Installs, sessions, app updates, revenue, attribution, deep links, SKAdNetwork and fraud signals are all handled natively — the JS layer only forwards your calls.

  • iOS 14+
  • Android 5+ (API 21+)
  • React Native ≥ 0.72 (classic bridge — NativeModules + NativeEventEmitter)

Installation

Bare React Native

npm install @appsynk/react-native-sdk
cd ios && pod install   # CocoaPods resolves the native 'AppSynk' pod automatically
# Android: nothing to do — autolinking + Maven Central resolve 'io.appsynk:sdk'

That's it — two commands. There are no extra peer dependencies to install: the native SDKs own storage, device info, advertising identifiers, the Play Install Referrer, SKAdNetwork and fraud detection.

Expo

⚠️ Expo Go is not supported. Like Adjust / AppsFlyer / LinkRunner, this SDK ships custom native modules, which Expo Go cannot load. You need a Dev Client.

npx expo install @appsynk/react-native-sdk

Add the config plugin to app.json / app.config.js:

{
  "expo": {
    "plugins": [
      ["@appsynk/react-native-sdk", {
        "userTrackingUsageDescription": "We use this data to measure our campaigns.",
        "facebookAppId": "1234567890",
        "deepLinkHost": "go.appsynk.io"
      }]
    ]
  }
}

Then generate the native project and build a Dev Client:

npx expo prebuild
# then: eas build --profile development   (or: npx expo run:ios / run:android)

expo prebuild injects everything the native SDKs require — no manual native edits:

  • iOSNSUserTrackingUsageDescription (ATT), SKAdNetworkItems, CFBundleURLTypes (appsynk://), and the applinks: Associated Domain.
  • AndroidINTERNET / ACCESS_NETWORK_STATE / AD_ID permissions, the Meta <queries>, the Facebook App ID <meta-data>, the backup rules, and the deep-link intent-filters (App Link + appsynk://).

Plugin options

| Option | Platform | Description | | --- | --- | --- | | userTrackingUsageDescription | iOS | ATT prompt copy. Omit it if you never call requestTrackingAuthorization(). | | facebookAppId | Android | Facebook App ID for Meta view-through (Install Referrer). | | deepLinkHost | both | App Link / Associated Domain host. Default go.appsynk.io. | | skAdNetworkIdentifiers | iOS | Override the default SKAdNetwork ID list. |


Quickstart

import AppSynk from '@appsynk/react-native-sdk';
import { Linking } from 'react-native';

// 1. Init — one line, in your entry point (index.js / App.tsx)
AppSynk.configure('ak_live_xxxxxxxxxxxxxxxxxxxxxxxx');

// 2. Deep links
Linking.getInitialURL().then((url) => url && AppSynk.handleDeepLink(url));
const sub = Linking.addEventListener('url', (e) => AppSynk.handleDeepLink(e.url));

// 3. Events
AppSynk.trackEvent('level_complete', { level: 5 });
AppSynk.trackRevenue(9.99, 'EUR', 'premium_monthly', 'ORDER_123');

// 4. Attribution + ATT (iOS)
const attribution = await AppSynk.getAttributionData();
const status = await AppSynk.requestTrackingAuthorization();

configure() automatically tracks, natively: first-install, reinstall, app_open, sessions, app_update, device info, advertising IDs, the Play Install Referrer (Android), and registers SKAdNetwork (iOS).

The environment (production vs sandbox) is auto-detected from the key prefix (ak_live_ / ak_sandbox_).


Configuration

import AppSynk, { AppSynkOptions } from '@appsynk/react-native-sdk';

const options: AppSynkOptions = {
  logLevel: 'debug',          // 'none' | 'error' | 'debug' | 'verbose'
  // environment: 'sandbox',  // optional — auto-detected from the key prefix
  // sessionTimeout: 1800,    // seconds (default 1800)
  // batchSize: 10,           // events per flush (default 10)
  // flushInterval: 30,       // seconds (default 30)
  // anonymizeUserByDefault: true, // start anonymized until consent (see GDPR)
};

AppSynk.configure('ak_sandbox_xxxxxxxxxxxxxxxxxxxxxxxx', options);

Options mirror the native AppSynkOptions 1:1 (durations are in seconds) and are forwarded as-is; each platform ignores the options that don't apply to it (e.g. disableIdfa on Android, disableGaid on iOS).

| Option | Type | Default | Notes | | --- | --- | --- | --- | | environment | 'production' \| 'sandbox' | auto | Detected from the key prefix when omitted. | | logLevel | 'none' \| 'error' \| 'debug' \| 'verbose' | 'none' | | | customApiUrl | string | — | Data residency / self-hosted gateway. | | sessionTimeout | number (s) | 1800 | Inactivity before a new session. | | batchSize | number | 10 | Events buffered before a flush. | | flushInterval | number (s) | 30 | Auto-flush interval. | | attWaitTimeout | number (s) | 60 | iOS — wait for the ATT decision before the gated install. | | referrerWaitTimeout | number (s) | 10 | Android — wait for Play Referrer + GAID before the install. | | disableIdfa / disableIdfv / disableAdServices / disableSKAdNetwork | boolean | false | iOS privacy toggles. | | disableGaid / disableAndroidId / disableMetaReferrer | boolean | false | Android privacy toggles. | | anonymizeUserByDefault | boolean | false | Start anonymized until consent. | | hmacSecretKey / hmacKeyId | string | — | Optional HMAC-SHA256 request signing. |


Events

import AppSynk, { STANDARD_EVENTS } from '@appsynk/react-native-sdk';

// Simple event
AppSynk.trackEvent('tutorial_completed');

// Event with properties
AppSynk.trackEvent('level_completed', {
  level: 5,
  score: 1250,
  difficulty: 'hard',
});

STANDARD_EVENTS exposes the canonical event names to avoid typos.


Revenue

trackRevenue records a one-time purchase. orderId is required and de-duplicated for 24 h natively.

AppSynk.trackRevenue(9.99, 'EUR', 'premium_monthly', 'ORDER_123');

// Ad impression revenue
AppSynk.trackAdRevenue({
  network: 'admob',
  amount: 0.0042,
  currency: 'USD',
  adUnit: 'banner_main',
  adType: 'banner', // 'banner' | 'interstitial' | 'rewarded'
});

For subscriptions, renewals, trials and refunds, use trackEvent with the standard event name (the convention the backend understands):

import AppSynk, { STANDARD_EVENTS } from '@appsynk/react-native-sdk';

AppSynk.trackEvent(STANDARD_EVENTS.SUBSCRIPTION_STARTED, {
  amount: 9.99,
  currency: 'EUR',
  product_id: 'premium_monthly',
  order_id: 'SUB_001',
  period: 'monthly',
});

AppSynk.trackEvent(STANDARD_EVENTS.REFUND, {
  amount: 9.99,
  currency: 'EUR',
  product_id: 'premium_monthly',
  order_id: 'SUB_001',
});

User identification

Call this after login so you don't pollute the install event.

AppSynk.setUserId('user_internal_12345');

AppSynk.setUserProperties({
  plan: 'premium',
  country: 'FR',
});

// On logout — clears userId + user properties, keeps the stable device_id
AppSynk.reset();

Deep linking

import { Linking } from 'react-native';
import AppSynk from '@appsynk/react-native-sdk';

useEffect(() => {
  // Cold start
  Linking.getInitialURL().then((url) => {
    if (url) AppSynk.handleDeepLink(url);
  });

  // Warm start
  const sub = Linking.addEventListener('url', ({ url }) => {
    AppSynk.handleDeepLink(url);
  });

  // Deferred deep links (resolved after install attribution)
  const dl = AppSynk.addDeepLinkListener((data) => {
    if (data.deepLink) navigation.navigate(data.deepLink);
  });

  return () => {
    sub.remove();
    dl.remove();
  };
}, []);

handleDeepLink(url) returns the resolved attribution; addDeepLinkListener fires for deferred deep links (resolved by the backend after install). addAttributionListener fires when attribution becomes available.

const attribution = await AppSynk.getAttributionData();
if (attribution && !attribution.isOrganic) {
  console.log('Channel:', attribution.channel);       // e.g. "tiktok_ads"
  console.log('Campaign:', attribution.campaignName);
  console.log('Deep link:', attribution.deepLink);
}

AttributionData fields: channel, campaignName, adSetName, creativeName, medium, source, clickId, clickTimestamp, isOrganic, attributionModel, confidenceScore, deepLink.


App Tracking Transparency (iOS)

AppSynk never shows the ATT prompt on its own. Call it at the right UX moment — after onboarding, not on the first launch.

// after onboarding
const status = await AppSynk.requestTrackingAuthorization();
// 'authorized' | 'denied' | 'restricted' | 'not_determined' (iOS)
// 'not_applicable' (Android)

Set userTrackingUsageDescription (Expo plugin) or NSUserTrackingUsageDescription (bare) so the prompt can be shown. Without ATT, AppSynk attributes using the IDFV plus probabilistic matching, and SKAdNetwork keeps working independently.


Privacy & GDPR

// Collect nothing identifying until consent: start anonymized
AppSynk.configure('ak_live_xxx', { anonymizeUserByDefault: true });

// When the user accepts your consent banner
AppSynk.setConsent({
  isUserSubjectToGDPR: true,
  hasConsentForDataUsage: true,
  hasConsentForAdsPersonalization: true,
});
AppSynk.anonymizeUser(false); // resume sending identifiers

// If the user refuses ad personalization
AppSynk.setConsent({
  isUserSubjectToGDPR: true,
  hasConsentForDataUsage: true,           // analytics OK
  hasConsentForAdsPersonalization: false, // no ad targeting
});
AppSynk.anonymizeUser(true);
  • anonymizeUser(true) strips advertising identifiers from future events and marks them anonymized. Data already sent is unaffected.
  • When hasConsentForAdsPersonalization is false, the backend skips Meta / TikTok postbacks that carry personal identifiers.

API reference

| Method | Returns | Notes | | --- | --- | --- | | configure(apiKey, options?) | void | Call once at startup. | | trackEvent(name, properties?) | void | | | trackRevenue(amount, currency, productId, orderId) | void | 24 h dedup on orderId. | | trackAdRevenue(params) | void | { network, amount, currency, adUnit?, adType? } | | setUserId(userId) | void | | | setUserProperties(properties) | void | | | setConsent(consent) | void | { isUserSubjectToGDPR, hasConsentForDataUsage, hasConsentForAdsPersonalization } | | anonymizeUser(enabled) | void | | | reset() | void | Clears identity, keeps device_id. | | flush() | void | No-op on iOS (auto-flush). | | getAttributionData() | Promise<AttributionData \| null> | | | getDeepLinkData() | Promise<AttributionData \| null> | | | handleDeepLink(url) | Promise<AttributionData \| null> | | | requestTrackingAuthorization() | Promise<string> | iOS ATT status; 'not_applicable' on Android. | | addDeepLinkListener(cb) | EmitterSubscription | .remove() to unsubscribe. | | addAttributionListener(cb) | EmitterSubscription | .remove() to unsubscribe. |


Troubleshooting

[AppSynk] Native module 'AppSynkBridge' not found → The native module isn't linked. Bare: re-run cd ios && pod install and rebuild; on Android, rebuild the app. Expo: you must run npx expo prebuild and launch a Dev Client — the SDK does not run in Expo Go. Every call no-ops (without crashing) until the native module is present.

Invalid API key / app not found → Check the key (live vs sandbox) and that your bundle ID matches the one registered in the AppSynk dashboard. Enable logLevel: 'debug' to see the native validation log.

Events don't appear → Events are batched (every flushInterval seconds or every batchSize events). Call AppSynk.flush() to force a send. In sandbox, events appear under Sandbox data in the dashboard.

GAID is null on Android → The user opted out of ad personalization. This is expected — AppSynk falls back to probabilistic attribution.