> ## Documentation Index
> Fetch the complete documentation index at: https://docs.datalyr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile SDKs

> Track events in iOS and React Native apps with DATALYR Mobile SDKs

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:**

```swift theme={null}
dependencies: [
  .package(url: "https://github.com/datalyr/swift", from: "1.3.0")
]
```

### Basic Usage

**Initialize SDK:**

```swift theme={null}
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:**

```swift theme={null}
// 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:**

```swift theme={null}
// After login
await DatalyrSDK.shared.identify("user_123", properties: [
  "email": "user@example.com",
  "name": "John Doe",
  "plan": "pro"
])
```

**Track Screens:**

```swift theme={null}
// Track screen view
await DatalyrSDK.shared.screen("Home Screen", properties: [
  "tab": "feed"
])
```

**Reset on Logout:**

```swift theme={null}
// Clear user identity
await DatalyrSDK.shared.reset()
```

### Configuration Options

```swift theme={null}
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:**

```swift theme={null}
// Use template for automatic conversion value encoding
try await DatalyrSDK.initializeWithSKAdNetwork(
  config: config,
  template: "ecommerce"
)
```

**Track with Automatic Encoding:**

```swift theme={null}
// 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:**

```swift theme={null}
// 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:**

```swift theme={null}
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:**

```swift theme={null}
let attribution = DatalyrSDK.shared.getAttributionData()
print("Attribution source: \(attribution.source ?? "unknown")")
```

**Set Attribution Manually:**

```swift theme={null}
await DatalyrSDK.shared.setAttributionData(AttributionData(
  source: "facebook",
  medium: "cpc",
  campaign: "summer_sale"
))
```

### Session Management

**Get Session Info:**

```swift theme={null}
// Current session ID
let sessionId = DatalyrSDK.shared.getStatus().sessionId

// Current session data
let session = DatalyrSDK.shared.getCurrentSession()
```

**Manual Session Control:**

```swift theme={null}
// End session manually
await DatalyrSDK.shared.endSession()
```

### Advanced Features

**Flush Manually:**

```swift theme={null}
// Flush queued events immediately
await DatalyrSDK.shared.flush()
```

**Get SDK Status:**

```swift theme={null}
let status = DatalyrSDK.shared.getStatus()
print("Initialized: \(status.initialized)")
print("Visitor ID: \(status.visitorId)")
print("Queue size: \(status.queueStats.queueSize)")
```

**Get Anonymous ID:**

```swift theme={null}
let anonId = DatalyrSDK.shared.getAnonymousId()
```

**Track Revenue Events:**

```swift theme={null}
await DatalyrSDK.shared.trackRevenue("subscription_renewal", properties: [
  "value": 9.99,
  "currency": "USD",
  "plan": "monthly"
])
```

### SwiftUI Integration

```swift theme={null}
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

```bash theme={null}
npm install @datalyr/react-native
```

**Requirements:**

* React Native 0.60+
* iOS 13+ / Android 5.0+

### Basic Usage

**Initialize SDK:**

```javascript theme={null}
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:**

```javascript theme={null}
// 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:**

```javascript theme={null}
// After login
await Datalyr.identify('user_123', {
  email: 'user@example.com',
  name: 'John Doe',
  plan: 'pro'
});
```

**Track Screens:**

```javascript theme={null}
// Track screen view
await Datalyr.screen('HomeScreen', {
  tab: 'feed'
});
```

**Reset on Logout:**

```javascript theme={null}
// Clear user identity
await Datalyr.reset();
```

### Configuration Options

```javascript theme={null}
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:**

```javascript theme={null}
// 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:**

```javascript theme={null}
// 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:**

```javascript theme={null}
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:**

```javascript theme={null}
const attribution = Datalyr.getAttributionData();
console.log('Attribution source:', attribution.source);
```

**Set Attribution Manually:**

```javascript theme={null}
await Datalyr.setAttributionData({
  source: 'facebook',
  medium: 'cpc',
  campaign: 'summer_sale'
});
```

### Session Management

**Get Session Info:**

```javascript theme={null}
const status = Datalyr.getStatus();
console.log('Session ID:', status.sessionId);

const session = Datalyr.getCurrentSession();
console.log('Session started:', session.startTime);
```

**Manual Session Control:**

```javascript theme={null}
// End session manually
await Datalyr.endSession();
```

### Advanced Features

**Flush Manually:**

```javascript theme={null}
// Flush queued events immediately
await Datalyr.flush();
```

**Get SDK Status:**

```javascript theme={null}
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:**

```javascript theme={null}
const anonId = Datalyr.getAnonymousId();
```

**Alias User:**

```javascript theme={null}
// Connect anonymous user to known user
await Datalyr.alias('user_123', 'previous_id');
```

### React Navigation Integration

```javascript theme={null}
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:

```typescript theme={null}
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

<Warning>
  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](/integrations/superwall) or [RevenueCat](/integrations/revenuecat) webhook integration — these send server-side events only when real money changes hands, with accurate revenue, trial status, and refund data.
</Warning>

**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

```javascript theme={null}
// 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)

```javascript theme={null}
// 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)

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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 Event            | Adjust Event            | DATALYR Method                |
| -------------------------- | ----------------------- | ----------------------------- |
| `af_purchase`              | `PURCHASE`              | `trackPurchase()`             |
| `af_add_to_cart`           | `ADD_TO_CART`           | `trackAddToCart()`            |
| `af_initiated_checkout`    | `INITIATE_CHECKOUT`     | `trackInitiateCheckout()`     |
| `af_complete_registration` | `COMPLETE_REGISTRATION` | `trackCompleteRegistration()` |
| `af_content_view`          | `VIEW_CONTENT`          | `trackViewContent()`          |
| `af_search`                | `SEARCH`                | `trackSearch()`               |
| `af_subscribe`             | `SUBSCRIBE`             | `trackSubscription()`         |

### 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:**

```javascript theme={null}
const status = Datalyr.getStatus();
console.log('Initialized:', status.initialized);
console.log('Queue size:', status.queueStats.queueSize);
```

**Enable Debug Mode:**

```javascript theme={null}
await Datalyr.initialize({
  apiKey: 'dk_your_api_key',
  debug: true,
});
```

**Force Flush Events:**

```javascript theme={null}
await Datalyr.flush();
```

**Verify API Key:**

* Should start with `dk_`
* Check Settings → API Keys in dashboard

### iOS Build Errors

```bash theme={null}
cd ios
pod deintegrate
pod cache clean --all
pod install
```

### Android Build Errors

```bash theme={null}
cd android && ./gradlew clean
npx react-native run-android
```

### Attribution Not Captured

Enable attribution in config:

```javascript theme={null}
await Datalyr.initialize({
  apiKey: 'dk_your_api_key',
  enableAttribution: true,
});
```

Check data:

```javascript theme={null}
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+)

```javascript theme={null}
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:**

```javascript theme={null}
import { Datalyr } from '@datalyr/react-native';

const attrs = Datalyr.getSuperwallAttributes();
// Pass to Superwall
Superwall.setUserAttributes(attrs);
```

**iOS Swift:**

```swift theme={null}
let attrs = DatalyrSDK.shared.getSuperwallAttributes()
Superwall.shared.setUserAttributes(attrs)
```

**Returned attributes:**

| Key            | Description              |
| -------------- | ------------------------ |
| `datalyr_id`   | DATALYR visitor ID       |
| `media_source` | Attribution source       |
| `campaign`     | Campaign name            |
| `adgroup`      | Ad group or UTM content  |
| `ad`           | Ad ID                    |
| `keyword`      | Search keyword           |
| `utm_source`   | UTM source               |
| `utm_medium`   | UTM medium               |
| `utm_campaign` | UTM campaign             |
| `fbclid`       | Facebook click ID        |
| `gclid`        | Google click ID          |
| `ttclid`       | TikTok click ID          |
| `idfa`         | iOS advertising ID       |
| `gaid`         | Google advertising ID    |
| `att_status`   | ATT authorization status |

### RevenueCat

Push DATALYR attribution data into RevenueCat subscriber attributes:

**React Native:**

```javascript theme={null}
import { Datalyr } from '@datalyr/react-native';
import Purchases from 'react-native-purchases';

const attrs = Datalyr.getRevenueCatAttributes();
Purchases.setAttributes(attrs);
```

**iOS Swift:**

```swift theme={null}
let attrs = DatalyrSDK.shared.getRevenueCatAttributes()
Purchases.shared.attribution.setAttributes(attrs)
```

**Returned attributes:**

| Key                 | Description           |
| ------------------- | --------------------- |
| `$datalyrId`        | DATALYR visitor ID    |
| `$mediaSource`      | Attribution source    |
| `$campaign`         | Campaign name         |
| `$adGroup`          | Ad group              |
| `$ad`               | Ad ID                 |
| `$keyword`          | Search keyword        |
| `$idfa`             | iOS advertising ID    |
| `$gpsAdId`          | Google advertising ID |
| `$attConsentStatus` | ATT consent status    |
| `utm_source`        | UTM source            |
| `utm_medium`        | UTM medium            |
| `utm_campaign`      | UTM campaign          |
| `fbclid`            | Facebook click ID     |
| `gclid`             | Google click ID       |
| `ttclid`            | TikTok click ID       |

[Learn more about Superwall integration](/integrations/superwall)
[Learn more about RevenueCat integration](/integrations/revenuecat)

## 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

<CardGroup cols={2}>
  <Card title="Web SDK" icon="globe" href="/sdks/web">
    Browser tracking guide
  </Card>

  <Card title="Server SDK" icon="server" href="/sdks/server">
    Backend tracking guide
  </Card>

  <Card title="Advanced Topics" icon="code" href="/sdks/advanced">
    Identity, batching, plugins
  </Card>

  <Card title="Attribution Reports" icon="chart-line" href="/features/attribution-reports">
    View attribution data
  </Card>
</CardGroup>
