Skip to main content
DATALYR provides native SDKs for iOS (Swift) and React Native, enabling mobile app tracking with attribution, SKAdNetwork support, and offline queuing.

What It Does

Mobile App Tracking:
  • iOS SDK (Swift) for native Apple apps
  • React Native SDK for cross-platform apps
  • Automatic session tracking
  • Attribution tracking (App Store, organic)
  • SKAdNetwork conversion value encoding
  • Offline event queue
  • App lifecycle tracking

iOS SDK (Swift)

Installation

Swift Package Manager: Xcode:
  1. File → Add Packages
  2. Enter: https://github.com/datalyr/swift
  3. Select version: 1.0.0+
Package.swift:
dependencies: [
  .package(url: "https://github.com/datalyr/swift", from: "1.3.0")
]

Basic Usage

Initialize SDK:
import DatalyrSDK

// In AppDelegate or App struct
try await DatalyrSDK.shared.initialize(config: DatalyrConfig(
  workspaceId: "your_workspace_id",
  apiKey: "dk_your_api_key",
  debug: true
))
Track Events:
// Track custom event
await DatalyrSDK.shared.track("button_tap", eventData: [
  "button_name": "signup",
  "screen": "onboarding"
])

// Track with revenue
await DatalyrSDK.shared.track("purchase", eventData: [
  "revenue": 99.99,
  "currency": "USD",
  "product_id": "prod_123"
])
Identify Users:
// After login
await DatalyrSDK.shared.identify("user_123", properties: [
  "email": "[email protected]",
  "name": "John Doe",
  "plan": "pro"
])
Track Screens:
// Track screen view
await DatalyrSDK.shared.screen("Home Screen", properties: [
  "tab": "feed"
])
Reset on Logout:
// Clear user identity
await DatalyrSDK.shared.reset()

Configuration Options

let config = DatalyrConfig(
  workspaceId: "your_workspace_id",
  apiKey: "dk_your_api_key",
  debug: false,
  endpoint: "https://ingest.datalyr.com/track",

  // Queue settings
  maxRetries: 3,
  retryDelay: 1000,
  timeout: 15000,
  batchSize: 10,
  flushInterval: 30000,
  maxQueueSize: 100,

  // Privacy
  respectDoNotTrack: true,

  // Auto-events
  enableAutoEvents: true,
  enableAttribution: true,

  // SKAdNetwork
  skadTemplate: "ecommerce"  // or "gaming", "subscription"
)

SKAdNetwork Support

Initialize with SKAdNetwork:
// Use template for automatic conversion value encoding
try await DatalyrSDK.initializeWithSKAdNetwork(
  config: config,
  template: "ecommerce"
)
Track with Automatic Encoding:
// Automatically updates SKAdNetwork conversion value
await DatalyrSDK.trackWithSKAdNetwork("purchase", eventData: [
  "revenue": 99.99,
  "currency": "USD"
])

// Convenience method for purchases
await DatalyrSDK.trackPurchase(
  value: 99.99,
  currency: "USD",
  productId: "prod_123"
)

// Convenience method for subscriptions
await DatalyrSDK.trackSubscription(
  value: 9.99,
  currency: "USD",
  plan: "monthly"
)
Test Conversion Values:
// Get conversion value without sending to Apple
let value = DatalyrSDK.getConversionValue(
  for: "purchase",
  properties: ["revenue": 99.99]
)
print("Conversion value: \(value ?? 0)")
Available Templates:
  • "ecommerce": Revenue-based encoding (0-6 brackets)
  • "gaming": Level progression and IAP
  • "subscription": Trial, active, churned states

Auto-Events

Automatically Tracked:
  • app_install: First launch
  • app_open: App foreground
  • app_close: App background
  • session_start: New session
  • session_end: Session timeout
Enable/Disable:
let config = DatalyrConfig(
  workspaceId: "your_workspace_id",
  apiKey: "dk_your_api_key",
  enableAutoEvents: false  // Disable auto-events
)

Attribution

Automatic Attribution: SDK captures attribution data on install:
  • App Store referrer
  • Campaign parameters
  • Install timestamp
Get Attribution Data:
let attribution = DatalyrSDK.shared.getAttributionData()
print("Attribution source: \(attribution.source ?? "unknown")")
Set Attribution Manually:
await DatalyrSDK.shared.setAttributionData(AttributionData(
  source: "facebook",
  medium: "cpc",
  campaign: "summer_sale"
))

Session Management

Get Session Info:
// Current session ID
let sessionId = DatalyrSDK.shared.getStatus().sessionId

// Current session data
let session = DatalyrSDK.shared.getCurrentSession()
Manual Session Control:
// End session manually
await DatalyrSDK.shared.endSession()

Advanced Features

Flush Manually:
// Flush queued events immediately
await DatalyrSDK.shared.flush()
Get SDK Status:
let status = DatalyrSDK.shared.getStatus()
print("Initialized: \(status.initialized)")
print("Visitor ID: \(status.visitorId)")
print("Queue size: \(status.queueStats.queueSize)")
Get Anonymous ID:
let anonId = DatalyrSDK.shared.getAnonymousId()
Track Revenue Events:
await DatalyrSDK.shared.trackRevenue("subscription_renewal", properties: [
  "value": 9.99,
  "currency": "USD",
  "plan": "monthly"
])

SwiftUI Integration

import SwiftUI
import DatalyrSDK

@main
struct MyApp: App {
  init() {
    Task {
      try await DatalyrSDK.shared.initialize(config: DatalyrConfig(
        workspaceId: "your_workspace_id",
        apiKey: "dk_your_api_key"
      ))
    }
  }

  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}

struct ContentView: View {
  var body: some View {
    Button("Track Event") {
      Task {
        await DatalyrSDK.shared.track("button_tap")
      }
    }
  }
}

React Native SDK

Installation

npm install @datalyr/react-native
Requirements:
  • React Native 0.60+
  • iOS 13+ / Android 5.0+

Basic Usage

Initialize SDK:
import { Datalyr } from '@datalyr/react-native';

// In App.tsx or index.js
await Datalyr.initialize({
  workspaceId: 'your_workspace_id',
  apiKey: 'dk_your_api_key',
  debug: true
});
Track Events:
// Track custom event
await Datalyr.track('button_tap', {
  button_name: 'signup',
  screen: 'onboarding'
});

// Track with revenue
await Datalyr.track('purchase', {
  revenue: 99.99,
  currency: 'USD',
  product_id: 'prod_123'
});
Identify Users:
// After login
await Datalyr.identify('user_123', {
  email: '[email protected]',
  name: 'John Doe',
  plan: 'pro'
});
Track Screens:
// Track screen view
await Datalyr.screen('HomeScreen', {
  tab: 'feed'
});
Reset on Logout:
// Clear user identity
await Datalyr.reset();

Configuration Options

await Datalyr.initialize({
  workspaceId: 'your_workspace_id',
  apiKey: 'dk_your_api_key',
  debug: false,
  endpoint: 'https://ingest.datalyr.com/track',

  // Queue settings
  maxRetries: 3,
  retryDelay: 1000,
  timeout: 15000,
  batchSize: 10,
  flushInterval: 30000,
  maxQueueSize: 100,

  // Privacy
  respectDoNotTrack: true,

  // Auto-events
  enableAutoEvents: true,
  enableAttribution: true,

  // SKAdNetwork (iOS only)
  skadTemplate: 'ecommerce'
});

SKAdNetwork Support (iOS)

Track with Automatic Encoding:
// Automatically updates SKAdNetwork conversion value (iOS only)
await Datalyr.trackWithSKAdNetwork('purchase', {
  revenue: 99.99,
  currency: 'USD'
});

// Convenience method for purchases
await Datalyr.trackPurchase(99.99, 'USD', 'prod_123');

// Convenience method for subscriptions
await Datalyr.trackSubscription(9.99, 'USD', 'monthly');
Test Conversion Values:
// Get conversion value without sending to Apple
const value = Datalyr.getConversionValue('purchase', { revenue: 99.99 });
console.log('Conversion value:', value);

Auto-Events

Automatically Tracked:
  • app_install: First launch
  • app_open: App foreground
  • app_close: App background
  • session_start: New session
  • session_end: Session timeout
Configuration:
await Datalyr.initialize({
  workspaceId: 'your_workspace_id',
  apiKey: 'dk_your_api_key',
  enableAutoEvents: true,
  autoEventConfig: {
    trackAppLifecycle: true,
    trackScreenViews: true,
    trackDeepLinks: true,
    sessionTimeout: 1800000  // 30 minutes
  }
});

Attribution

Get Attribution Data:
const attribution = Datalyr.getAttributionData();
console.log('Attribution source:', attribution.source);
Set Attribution Manually:
await Datalyr.setAttributionData({
  source: 'facebook',
  medium: 'cpc',
  campaign: 'summer_sale'
});

Session Management

Get Session Info:
const status = Datalyr.getStatus();
console.log('Session ID:', status.sessionId);

const session = Datalyr.getCurrentSession();
console.log('Session started:', session.startTime);
Manual Session Control:
// End session manually
await Datalyr.endSession();

Advanced Features

Flush Manually:
// Flush queued events immediately
await Datalyr.flush();
Get SDK Status:
const status = Datalyr.getStatus();
console.log('Initialized:', status.initialized);
console.log('Visitor ID:', status.visitorId);
console.log('Queue size:', status.queueStats.queueSize);
Get Anonymous ID:
const anonId = Datalyr.getAnonymousId();
Alias User:
// Connect anonymous user to known user
await Datalyr.alias('user_123', 'previous_id');

React Navigation Integration

import { NavigationContainer } from '@react-navigation/native';
import { Datalyr } from '@datalyr/react-native';

function App() {
  const navigationRef = useRef();
  const routeNameRef = useRef();

  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={() => {
        routeNameRef.current = navigationRef.current.getCurrentRoute().name;
      }}
      onStateChange={async () => {
        const previousRouteName = routeNameRef.current;
        const currentRouteName = navigationRef.current.getCurrentRoute().name;

        if (previousRouteName !== currentRouteName) {
          // Track screen view
          await Datalyr.screen(currentRouteName);
        }

        routeNameRef.current = currentRouteName;
      }}
    >
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Profile" component={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

TypeScript Support

Full TypeScript definitions included:
import { Datalyr, DatalyrConfig, EventData } from '@datalyr/react-native';

const config: DatalyrConfig = {
  workspaceId: 'your_workspace_id',
  apiKey: 'dk_your_api_key',
  debug: true
};

await Datalyr.initialize(config);

const eventData: EventData = {
  revenue: 99.99,
  currency: 'USD'
};

await Datalyr.track('purchase', eventData);

Revenue Tracking: SDK vs Webhooks

If you use Superwall or RevenueCat, do not use trackPurchase() or trackSubscription() for revenue attribution. These methods fire client-side before payment is confirmed, meaning trials and failed payments will be counted as revenue.Instead, use the Superwall or RevenueCat webhook integration — these send server-side events only when real money changes hands, with accurate revenue, trial status, and refund data.
With Superwall/RevenueCat (recommended for subscription apps):
  • Use the DATALYR SDK for behavioral events only: track('paywall_view'), track('trial_start'), screen(), identify(), etc.
  • Use the webhook integration for revenue events: purchases, renewals, cancellations, refunds
  • Pass attribution to Superwall/RevenueCat using getSuperwallAttributes() or getRevenueCatAttributes() so revenue events get linked back to the original campaign
Without Superwall/RevenueCat:
  • Use trackPurchase() and trackSubscription() only after confirming a real charge (not a trial start)
  • These methods are appropriate for one-time purchases or when you handle billing directly

Common Use Cases

E-commerce App

// Product view
await Datalyr.track('product_view', {
  product_id: 'prod_123',
  name: 'Premium Widget',
  price: 99.99
});

// Add to cart
await Datalyr.track('add_to_cart', {
  product_id: 'prod_123',
  quantity: 2
});

// Purchase (with SKAdNetwork on iOS)
// Only use if NOT using Superwall/RevenueCat for revenue
await Datalyr.trackPurchase(199.98, 'USD', 'prod_123');

Subscription App (with Superwall/RevenueCat)

// Use SDK for behavioral events
await Datalyr.track('paywall_view', { paywall_id: 'main' });
await Datalyr.track('trial_start', { plan: 'pro', duration: 14 });

// Pass attribution to Superwall/RevenueCat
const attrs = Datalyr.getSuperwallAttributes(); // or getRevenueCatAttributes()
Superwall.setUserAttributes(attrs);

// Revenue is tracked automatically via webhooks — don't call trackPurchase()

Subscription App (without Superwall/RevenueCat)

// Trial start — no revenue, so just track the event
await Datalyr.track('trial_start', {
  plan: 'pro',
  duration: 14
});

// Only track revenue after confirming a real charge
await Datalyr.trackSubscription(9.99, 'USD', 'monthly');

// Subscription cancel
await Datalyr.track('subscription_cancel', {
  plan: 'monthly',
  reason: 'too_expensive'
});

Gaming App

// Level complete
await Datalyr.trackWithSKAdNetwork('level_complete', {
  level: 5,
  score: 1250,
  time: 180
});

// In-app purchase
await Datalyr.trackPurchase(4.99, 'USD', 'coins_1000');

// Achievement unlocked
await Datalyr.track('achievement', {
  achievement_id: 'first_win',
  points: 100
});

Best Practices

Initialize Early: Initialize SDK in AppDelegate (iOS) or index.js (React Native) before any events. Flush Before Exit: SDK automatically flushes on app background, but manually call flush() for critical events. Handle Offline: SDK queues events offline and sends when connection restored. Test Conversion Values: Use getConversionValue() to test SKAdNetwork encoding before production. Privacy: Enable respectDoNotTrack and collect user consent before tracking.

Migrating from AppsFlyer / Adjust

DATALYR provides similar functionality to AppsFlyer and Adjust with a simpler integration and bundled platform SDKs.

From AppsFlyer

// BEFORE: AppsFlyer
import appsFlyer from 'react-native-appsflyer';
appsFlyer.initSdk({ devKey: 'YOUR_DEV_KEY', appId: 'YOUR_APP_ID' });
appsFlyer.logEvent('af_purchase', { af_revenue: 99.99, af_currency: 'USD' });

// AFTER: DATALYR
import { Datalyr } from '@datalyr/react-native';
await Datalyr.initialize({ apiKey: 'dk_your_api_key' });
await Datalyr.trackPurchase(99.99, 'USD', 'product_id');

From Adjust

// BEFORE: Adjust
import { Adjust, AdjustConfig, AdjustEvent } from 'react-native-adjust';
const config = new AdjustConfig('YOUR_TOKEN', AdjustConfig.EnvironmentProduction);
Adjust.create(config);
const event = new AdjustEvent('abc123');
event.setRevenue(99.99, 'USD');
Adjust.trackEvent(event);

// AFTER: DATALYR
import { Datalyr } from '@datalyr/react-native';
await Datalyr.initialize({ apiKey: 'dk_your_api_key' });
await Datalyr.trackPurchase(99.99, 'USD');

Event Name Mapping

AppsFlyer EventAdjust EventDATALYR Method
af_purchasePURCHASEtrackPurchase()
af_add_to_cartADD_TO_CARTtrackAddToCart()
af_initiated_checkoutINITIATE_CHECKOUTtrackInitiateCheckout()
af_complete_registrationCOMPLETE_REGISTRATIONtrackCompleteRegistration()
af_content_viewVIEW_CONTENTtrackViewContent()
af_searchSEARCHtrackSearch()
af_subscribeSUBSCRIBEtrackSubscription()

Migration Benefits

  • No Ad SDKs Required: Uses native Apple and Google frameworks for IDFA and GAID
  • Unified API: Single SDK for all platforms and ad networks
  • Web-to-App: Automatic attribution matching via email
  • Flat Pricing: No per-MAU charges

Troubleshooting

Events Not Appearing

Check SDK Status:
const status = Datalyr.getStatus();
console.log('Initialized:', status.initialized);
console.log('Queue size:', status.queueStats.queueSize);
Enable Debug Mode:
await Datalyr.initialize({
  apiKey: 'dk_your_api_key',
  debug: true,
});
Force Flush Events:
await Datalyr.flush();
Verify API Key:
  • Should start with dk_
  • Check Settings → API Keys in dashboard

iOS Build Errors

cd ios
pod deintegrate
pod cache clean --all
pod install

Android Build Errors

cd android && ./gradlew clean
npx react-native run-android

Attribution Not Captured

Enable attribution in config:
await Datalyr.initialize({
  apiKey: 'dk_your_api_key',
  enableAttribution: true,
});
Check data:
const attribution = Datalyr.getAttributionData();
console.log(attribution);

SKAdNetwork Not Working

  • iOS 14.0+ required (iOS 16.1+ for SKAN 4.0)
  • Set skadTemplate in config
  • Use trackWithSKAdNetwork() for automatic encoding
  • Test with getConversionValue() first

App Tracking Transparency (iOS 14.5+)

import { requestTrackingPermissionsAsync } from 'expo-tracking-transparency';

const { status } = await requestTrackingPermissionsAsync();
await Datalyr.updateTrackingAuthorization(status === 'granted');

Third-Party Integrations

Superwall

Push DATALYR attribution data into Superwall user attributes: React Native:
import { Datalyr } from '@datalyr/react-native';

const attrs = Datalyr.getSuperwallAttributes();
// Pass to Superwall
Superwall.setUserAttributes(attrs);
iOS Swift:
let attrs = DatalyrSDK.shared.getSuperwallAttributes()
Superwall.shared.setUserAttributes(attrs)
Returned attributes:
KeyDescription
datalyr_idDATALYR visitor ID
media_sourceAttribution source
campaignCampaign name
adgroupAd group or UTM content
adAd ID
keywordSearch keyword
utm_sourceUTM source
utm_mediumUTM medium
utm_campaignUTM campaign
fbclidFacebook click ID
gclidGoogle click ID
ttclidTikTok click ID
idfaiOS advertising ID
gaidGoogle advertising ID
att_statusATT authorization status

RevenueCat

Push DATALYR attribution data into RevenueCat subscriber attributes: React Native:
import { Datalyr } from '@datalyr/react-native';
import Purchases from 'react-native-purchases';

const attrs = Datalyr.getRevenueCatAttributes();
Purchases.setAttributes(attrs);
iOS Swift:
let attrs = DatalyrSDK.shared.getRevenueCatAttributes()
Purchases.shared.attribution.setAttributes(attrs)
Returned attributes:
KeyDescription
$datalyrIdDATALYR visitor ID
$mediaSourceAttribution source
$campaignCampaign name
$adGroupAd group
$adAd ID
$keywordSearch keyword
$idfaiOS advertising ID
$gpsAdIdGoogle advertising ID
$attConsentStatusATT consent status
utm_sourceUTM source
utm_mediumUTM medium
utm_campaignUTM campaign
fbclidFacebook click ID
gclidGoogle click ID
ttclidTikTok click ID
Learn more about Superwall integration Learn more about RevenueCat integration

Platform Requirements

iOS SDK:
  • iOS 13.0+
  • Xcode 14+
  • Swift 5.5+
React Native SDK:
  • React Native 0.60+
  • iOS 13+ / Android 5.0+
  • Node.js 14+

Next Steps

Web SDK

Browser tracking guide

Server SDK

Backend tracking guide

Advanced Topics

Identity, batching, plugins

Attribution Reports

View attribution data