StageUp
Mobile SDKDeep Link

React Native

AdStage DeepLink Integration Guide (React Native)

Table of Contents

  1. Overview
  2. Installation
  3. Platform Configuration
  4. SDK Initialization
  5. Deep Link Handling
  6. Creating Deep Links
  7. Troubleshooting

Overview

AdStage DeepLink SDK for React Native provides the following features:

  • Real-time Deep Links: Instant processing via URL Scheme, App Link/Universal Links
  • Deferred Deep Links: Automatic restoration on first launch after app installation
  • Dynamic Deep Link Creation: Trackable link generation through server API
  • Attribution Tracking: Marketing analytics based on UTM parameters
  • Cross-Platform: Same API for Android/iOS
  • URL Scheme: myapp://promo/summer
  • Android App Links: https://go.myapp.com/abc123
  • iOS Universal Links: https://go.myapp.com/abc123
  • Deferred Deep Links: App not installed → Store → Install → Restored on app launch

Installation

npm / yarn Installation

# npm
npm install @adstage/react-native-sdk
 
# yarn
yarn add @adstage/react-native-sdk

iOS Dependency Installation

cd ios && pod install

Platform Configuration

iOS Configuration

1. ATT (App Tracking Transparency) Permission (Required)

Add the following to ios/YourApp/Info.plist:

<key>NSUserTrackingUsageDescription</key>
<string>This is used for measuring ad performance and providing personalized ads.</string>

2. URL Scheme Configuration

Add the following to ios/YourApp/Info.plist:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>your_app_scheme</string>
        </array>
    </dict>
</array>

Add the following to ios/YourApp/YourApp.entitlements:

<key>com.apple.developer.associated-domains</key>
<array>
    <string>applinks:go.yourapp.com</string>
</array>

4. AppDelegate.mm Modification

Add deep link handling methods to ios/YourApp/AppDelegate.mm:

#import <AdapterAdStage/AdapterAdStage-Swift.h>
 
// URL Scheme handling
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    // AdStage deep link handling
    [[DeepLinkManager shared] handleDeepLinkFrom:url];
    return YES;
}
 
// Universal Links handling
- (BOOL)application:(UIApplication *)application
    continueUserActivity:(NSUserActivity *)userActivity
      restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
    if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
        [[DeepLinkManager shared] handleUniversalLinkWithUserActivity:userActivity];
        return YES;
    }
    return NO;
}

Android Configuration

1. Add Maven Repository (Required)

Add to android/settings.gradle or android/build.gradle:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
        maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }
        maven { url "https://maven.adstage.io/repository/public" }
    }
}

2. minSdkVersion Configuration

Verify in android/build.gradle:

buildscript {
    ext {
        minSdkVersion = 24  // Minimum 24 required
    }
}

3. AndroidManifest.xml Configuration

Add Intent Filter to android/app/src/main/AndroidManifest.xml:

<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"
    android:exported="true">
    
    <!-- Default launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    
    <!-- URL Scheme deep link -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="your_app_scheme" />
    </intent-filter>
    
    <!-- App Links (HTTPS) -->
    <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="go.yourapp.com" />
    </intent-filter>
</activity>

4. launchMode Important Notes

android:launchMode="singleTask"
  • singleTask: Reuses existing Activity (Recommended)
  • ⚠️ singleTop: Reuses only when at top of stack
  • standard: Creates new instance each time (causes duplicate deep links)

SDK Initialization

Basic Initialization

import { AdStage } from '@adstage/react-native-sdk';
 
// Initialize on app start
const initializeAdStage = async () => {
  try {
    await AdStage.initialize({
      apiKey: 'your-api-key-here'
    });
    
    // Set up deep link listener
    setupDeepLinkListener();
    
    // Check pending deep link (cold start)
    AdStage.deepLink.checkPendingDeepLink();
    
    console.log('✅ AdStage SDK initialized');
  } catch (error) {
    console.error('❌ AdStage initialization failed:', error);
  }
};
 
// Call in App.tsx
useEffect(() => {
  initializeAdStage();
}, []);

import { AdStage } from '@adstage/react-native-sdk';
 
const setupDeepLinkListener = () => {
  // Unified deep link listener
  AdStage.deepLink.setListener((data) => {
    console.log('✅ Deep link received:', data);
    console.log('  - Short Path:', data.shortPath);
    console.log('  - Link ID:', data.linkId);
    console.log('  - Parameters:', data.parameters);
    
    // Handle business logic
    handleDeepLink(data);
  });
};
 
const handleDeepLink = (data: DeepLinkData) => {
  const { shortPath, parameters } = data;
  
  // Navigate based on parameters
  if (parameters?.product_id) {
    // Navigate to product detail screen
    navigation.navigate('ProductDetail', { 
      productId: parameters.product_id 
    });
  } else if (parameters?.campaign) {
    // Navigate to campaign screen
    navigation.navigate('Campaign', { 
      campaignId: parameters.campaign 
    });
  } else if (parameters?.promo) {
    // Apply promo code
    applyPromoCode(parameters.promo);
  } else {
    // Default: Home screen
    navigation.navigate('Home');
  }
};

2. DeepLinkData Structure

interface DeepLinkData {
  shortPath: string;           // Deep link short path (e.g., "abc123")
  linkId: string;              // Deep link unique ID
  source: DeepLinkSource;      // Deep link source (real-time/deferred)
  eventType: string;           // Event type
  parameters: Record<string, string>; // Custom parameters
  createdAt?: string;          // Creation time
}
 
type DeepLinkSource = 
  | 'url_scheme'      // Received via URL Scheme
  | 'app_link'        // Received via Android App Links
  | 'universal_link'  // Received via iOS Universal Links
  | 'deferred';       // Deferred deep link (restored after installation)

When the app is launched from a deep link while completely terminated:

// Check pending deep link after SDK initialization
await AdStage.initialize({ apiKey: 'your-api-key' });
 
// Set up listener first
AdStage.deepLink.setListener((data) => {
  console.log('Deep link:', data);
  handleDeepLink(data);
});
 
// Check and handle pending deep link
AdStage.deepLink.checkPendingDeepLink();

4. Practical Example: React Navigation Integration

import React, { useEffect } from 'react';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { AdStage } from '@adstage/react-native-sdk';
 
function App() {
  const navigationRef = React.useRef(null);
  
  useEffect(() => {
    const initSDK = async () => {
      await AdStage.initialize({ apiKey: 'your-api-key' });
      
      // Set up deep link listener
      AdStage.deepLink.setListener((data) => {
        const { parameters } = data;
        
        // Navigate with React Navigation
        if (navigationRef.current) {
          if (parameters?.screen) {
            navigationRef.current.navigate(parameters.screen, parameters);
          }
        }
      });
      
      // Check cold start deep link
      AdStage.deepLink.checkPendingDeepLink();
    };
    
    initSDK();
  }, []);
  
  return (
    <NavigationContainer ref={navigationRef}>
      {/* Navigation stack */}
    </NavigationContainer>
  );
}

import { AdStage } from '@adstage/react-native-sdk';
 
const createDeepLink = async () => {
  try {
    const result = await AdStage.deepLink.create({
      name: 'Summer Promotion Link',
      description: '2025 Summer Sale Event',
      
      // UTM parameters
      campaign: 'summer_sale_2025',
      source: 'instagram',
      medium: 'social',
      
      // Custom parameters
      parameters: {
        promo_code: 'SUMMER25',
        discount: '20',
        screen: 'PromoDetail',
      },
      
      // Redirect settings
      redirectType: 'app', // 'app' | 'store' | 'web'
      
      // Platform-specific settings
      androidConfig: {
        packageName: 'com.yourapp.android',
        scheme: 'yourapp',
        fallbackUrl: 'https://yourapp.com/promo',
      },
      iosConfig: {
        appStoreId: '1234567890',
        scheme: 'yourapp',
        fallbackUrl: 'https://yourapp.com/promo',
      },
      webConfig: {
        fallbackUrl: 'https://yourapp.com/promo',
      },
    });
    
    console.log('✅ Deep link created successfully');
    console.log('  - Short URL:', result.shortUrl);
    console.log('  - Short Path:', result.shortPath);
    console.log('  - Link ID:', result.linkId);
    
    // Share the link
    shareDeepLink(result.shortUrl);
    
  } catch (error) {
    console.error('❌ Deep link creation failed:', error);
  }
};

CreateDeepLinkRequest Parameters

ParameterTypeRequiredDescription
namestringDeep link name
descriptionstring-Deep link description
campaignstring-UTM campaign
sourcestring-UTM source
mediumstring-UTM medium
parametersobject-Custom parameters
redirectTypestring-Redirect type (app/store/web)
androidConfigobject-Android settings
iosConfigobject-iOS settings
webConfigobject-Web settings

RedirectType Description

type RedirectType = 'app' | 'store' | 'web';
TypeApp InstalledApp Not Installed
storeGo to StoreGo to Store
appLaunch App (real-time deep link)Go to Store → Install → Deferred deep link
webGo to Web URLGo to Web URL

Utility Functions

Extract Short Path

// Extract Short Path from URL
const shortPath = AdStage.deepLink.extractShortPath('https://go.adstage.io/ABCDEF');
console.log(shortPath); // "ABCDEF"

Manual Android Intent Handling

import { Linking } from 'react-native';
 
// Handle new Intent when app is already running on Android
Linking.addEventListener('url', ({ url }) => {
  // Automatically handled by native, but can be called manually if needed
  AdStage.deepLink.handleIntent();
});

Troubleshooting

iOS Issues

  1. Verify URL Scheme is correctly configured in Info.plist
  2. Verify deep link handling methods are added to AppDelegate.mm
  3. For Universal Links, verify Associated Domains are configured

ATT permission popup not showing

  • Verify NSUserTrackingUsageDescription key exists in Info.plist

Android Issues

  • Verify android:launchMode="singleTask" setting
  1. Verify Intent Filter in AndroidManifest.xml
  2. Verify android:exported="true" setting
  3. For App Links, verify Digital Asset Links file

Common Issues

  1. Verify listener is set after SDK initialization is complete
  2. Verify checkPendingDeepLink() is called
  3. Check network connection status

Parameters not being passed

  • Verify values are correctly set in the parameters object when creating deep links

Table of Contents