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

# React Native Deep Links

> Handle deferred deep links for first-launch routing and attribution

<Info>
  **Supported on:** iOS SDK `1.0.61+` · Android SDK `1.0.33+` · React Native SDK `1.0.4+`
</Info>

The deep links module handles deferred deep links -- routing users to the correct screen on first launch after installing from a link. This enables attribution tracking, referral programs, and contextual first-launch experiences.

## Deferred Deep Links

A deferred deep link works across the install boundary:

1. User clicks a link on the web (e.g., `https://yourapp.com/workout/123?ref=instagram`)
2. User is redirected to the App Store or Play Store and installs your app
3. On first launch, the SDK resolves the original link and returns the destination

```typescript theme={null}
import { AppDNA } from '@appdna/react-native-sdk';

const deepLink = await AppDNA.checkDeferredDeepLink();
if (deepLink) {
  // deepLink.screen = "/workout/123"
  // deepLink.params = { ref: "instagram" }
  navigate(deepLink.screen, deepLink.params);
}
```

<Info>
  `checkDeferredDeepLink` is a static method on `AppDNA`. It should be called once on first launch, typically after `configure()` completes. The SDK returns `null` if no deferred link is found or if the app was not installed from a link. On Android, resolution uses the Google Install Referrer API plus AppDNA's stored visitor context.
</Info>

## Deferred Deep Link Check

`checkDeferredDeepLink` is a top-level static method on `AppDNA`, not part of the deep links module:

```typescript theme={null}
const deepLink = await AppDNA.checkDeferredDeepLink();
if (deepLink) {
  navigate(deepLink.screen, deepLink.params);
}
```

## Module Access

```typescript theme={null}
const deepLinks = AppDNA.deepLinks;
```

### Module Methods

| Method        | Signature                                      | Description                                              |                                        |
| ------------- | ---------------------------------------------- | -------------------------------------------------------- | -------------------------------------- |
| `setDelegate` | \`setDelegate(delegate: AppDNADeepLinkDelegate | null): void\`                                            | Set a delegate for deep link callbacks |
| `handleURL`   | `handleURL(url: string): Promise<void>`        | Pass an incoming deep link URL to the SDK for processing |                                        |

## DeferredDeepLink

| Property    | Type                     | Description                                      |
| ----------- | ------------------------ | ------------------------------------------------ |
| `screen`    | `string`                 | Target screen path (e.g., `"/workout/123"`)      |
| `params`    | `Record<string, string>` | Additional parameters (e.g., referrer, campaign) |
| `visitorId` | `string`                 | The visitor identifier from the originating link |

## AppDNADeepLinkDelegate

Register a delegate for deep link events:

```typescript theme={null}
interface AppDNADeepLinkDelegate {
  /** Veto. Return false to suppress deep-link processing. */
  shouldOpen(url: string, params: Record<string, unknown>): boolean;

  onDeepLinkReceived(url: string, params: Record<string, unknown>): void;
}
```

`shouldOpen` is a veto called BEFORE the SDK processes a deep link. Return `false` to defer routing (for example until after login completes) and handle the URL yourself once the user is authenticated. Return `true` to let the SDK proceed with default handling.

### Example Implementation

```typescript theme={null}
import { AppDNA, AppDNADeepLinkDelegate } from '@appdna/react-native-sdk';

const linkHandler: AppDNADeepLinkDelegate = {
  shouldOpen(url, params) {
    // Return false to defer routing (e.g., until login completes).
    return true;
  },

  onDeepLinkReceived(url, params) {
    // Route based on the incoming URL
    const parsed = new URL(url);
    route(parsed.pathname, params);
  },
};

function route(screen: string, params: Record<string, unknown>): void {
  if (screen.startsWith('/workout/')) {
    const id = screen.substring('/workout/'.length);
    showWorkout(id);
  } else if (screen === '/referral') {
    showReferralWelcome(params.ref as string | undefined);
  } else {
    showHome();
  }
}

function showWorkout(id: string): void { /* ... */ }
function showReferralWelcome(referrer: string | undefined): void { /* ... */ }
function showHome(): void { /* ... */ }

AppDNA.deepLinks.setDelegate(linkHandler);
```

## Platform Setup

Deep links require platform-specific configuration in your iOS and Android host projects.

### iOS — Universal Links

To support Universal Links on iOS, configure your app:

1. Enable **Associated Domains** in your target's **Signing & Capabilities**.
2. Add your domain: `applinks:yourapp.com`
3. Host an `apple-app-site-association` file on your domain.

In your iOS host (`AppDelegate.swift` or `SceneDelegate.swift`), forward incoming URLs to the SDK:

```swift theme={null}
// SceneDelegate
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let url = userActivity.webpageURL {
        AppDNA.deepLinks.handleURL(url)
    }
}
```

### Android — App Links

1. Add intent filters to your `AndroidManifest.xml`:

```xml theme={null}
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="yourapp.com" />
</intent-filter>
```

2. Host a Digital Asset Links file at `https://yourapp.com/.well-known/assetlinks.json`.
3. In your `MainActivity.kt`, forward the incoming intent's data URL to the SDK:

```kotlin theme={null}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    intent?.dataString?.let { AppDNA.deepLinks.handleURL(it) }
}

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    intent.dataString?.let { AppDNA.deepLinks.handleURL(it) }
}
```

### Forwarding from TypeScript

If your app already parses deep links in TypeScript (e.g. via React Native's built-in `Linking` API or the `expo-linking` package), forward them to the SDK so triggers, attribution, and the delegate fire consistently:

```typescript theme={null}
import { Linking } from 'react-native';

// On initial launch:
const url = await Linking.getInitialURL();
if (url) {
  await AppDNA.deepLinks.handleURL(url);
}

// While the app is foreground:
Linking.addEventListener('url', ({ url }) => {
  AppDNA.deepLinks.handleURL(url);
});
```

## Auto-Tracked Events

| Event                         | Trigger                                          |
| ----------------------------- | ------------------------------------------------ |
| `deep_link_handled`           | A deep link URL is processed                     |
| `deferred_deep_link_resolved` | A deferred deep link is resolved on first launch |

## Full Example

```typescript theme={null}
import { AppDNA, AppDNADeepLinkDelegate } from '@appdna/react-native-sdk';

class AppCoordinator implements AppDNADeepLinkDelegate {
  constructor() {
    AppDNA.deepLinks.setDelegate(this);
  }

  async handleFirstLaunch(currentUserId: string): Promise<void> {
    // Check for deferred deep link on first launch
    const deepLink = await AppDNA.checkDeferredDeepLink();

    if (deepLink) {
      // User installed from a link -- route to the target screen
      this.route(deepLink.screen, deepLink.params);

      // Track the visitor as a user trait
      await AppDNA.identify(currentUserId, {
        visitor_id: deepLink.visitorId,
      });
    } else {
      // Normal install -- show default onboarding
      this.showOnboarding();
    }
  }

  // AppDNADeepLinkDelegate

  shouldOpen(url: string, params: Record<string, unknown>): boolean {
    return true;
  }

  onDeepLinkReceived(url: string, params: Record<string, unknown>): void {
    const parsed = new URL(url);
    this.route(parsed.pathname, params);
  }

  private route(screen: string, params: Record<string, unknown>): void {
    // Route to the correct screen based on the path
  }

  private showOnboarding(): void { /* ... */ }
}
```

<Note>
  Deep link routes are configured in the Console under **Engagement > Deep Links**. The SDK resolves deferred links by checking the Google Install Referrer (Android) plus AppDNA's stored visitor context on first launch.
</Note>
