StageUp
Mobile SDKDeep Link

React Native

AdStage DeepLink Integration Guide (React Native)

Table of Contents

  1. Overview
  2. Installation
  3. Platform-Specific Setup
  4. SDK Initialization
  5. Handling Incoming Deep Links
  6. Creating Deep Links
  7. Troubleshooting

Overview

The AdStage DeepLink SDK for React Native provides the following features:

  • Real-time deep links: Instant handling via URL Scheme, App Link/Universal Links
  • Deferred deep links: Automatic restoration on first launch after app installation
  • Dynamic deep link creation: Generation of trackable links via the server API
  • Attribution tracking: Marketing analysis based on UTM parameters
  • Cross-platform: Identical 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: When the app is 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-Specific Setup

iOS Setup

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

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

<key>NSUserTrackingUsageDescription</key>
<string>광고 성과 측정 및 개인화된 광고 제공을 위해 사용됩니다.</string>

2. URL Scheme Setup

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. Modifying AppDelegate.mm

In the ios/YourApp/AppDelegate.mm file, add the methods for handling deep links:

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

Android Setup

1. Adding the 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 Setup

Verify in android/build.gradle:

buildscript {
    ext {
        minSdkVersion = 24  // 최소 24 이상 필요
    }
}

3. AndroidManifest.xml Setup

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

<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"
    android:exported="true">
    
    <!-- 기본 런처 -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    
    <!-- URL Scheme 딥링크 -->
    <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 the existing Activity (recommended)
  • ⚠️ singleTop: Reuses only when at the top of the stack
  • standard: Creates a new instance every time (causes duplicate deep links)

SDK Initialization

Basic Initialization

import { AdStage } from '@adstage/react-native-sdk';
 
// 앱 시작 시 초기화
const initializeAdStage = async () => {
  try {
    await AdStage.initialize({
      apiKey: 'your-api-key-here'
    });
    
    // 딥링크 리스너 설정
    setupDeepLinkListener();
    
    // Pending 딥링크 확인 (콜드 스타트)
    AdStage.deepLink.checkPendingDeepLink();
    
    console.log('✅ AdStage SDK 초기화 완료');
  } catch (error) {
    console.error('❌ AdStage 초기화 실패:', error);
  }
};
 
// App.tsx에서 호출
useEffect(() => {
  initializeAdStage();
}, []);

import { AdStage } from '@adstage/react-native-sdk';
 
const setupDeepLinkListener = () => {
  // 통합 딥링크 리스너
  AdStage.deepLink.setListener((data) => {
    console.log('✅ 딥링크 수신:', data);
    console.log('  - Short Path:', data.shortPath);
    console.log('  - Link ID:', data.linkId);
    console.log('  - Parameters:', data.parameters);
    
    // 비즈니스 로직 처리
    handleDeepLink(data);
  });
};
 
const handleDeepLink = (data: DeepLinkData) => {
  const { shortPath, parameters } = data;
  
  // 파라미터에 따른 화면 이동
  if (parameters?.product_id) {
    // 상품 상세 화면으로 이동
    navigation.navigate('ProductDetail', { 
      productId: parameters.product_id 
    });
  } else if (parameters?.campaign) {
    // 캠페인 화면으로 이동
    navigation.navigate('Campaign', { 
      campaignId: parameters.campaign 
    });
  } else if (parameters?.promo) {
    // 프로모션 코드 적용
    applyPromoCode(parameters.promo);
  } else {
    // 기본: 홈 화면
    navigation.navigate('Home');
  }
};

2. DeepLinkData Structure

interface DeepLinkData {
  linkId: string;              // 딥링크 고유 ID (서버 발급)
  shortPath: string;           // 딥링크 short path (예: "abc123")
  parameters: Record<string, string>; // 커스텀 파라미터
  source: DeepLinkSource;      // 딥링크 소스 (실시간/디퍼드)
  eventType: string;           // 이벤트 타입 (예: "OPEN", "INSTALL")
  timestamp?: string;          // 타임스탬프
}
 
type DeepLinkSource =
  | 'realtime'   // 실시간 딥링크 (앱 실행 중/URL·Universal Link 수신)
  | 'install'    // 디퍼드 딥링크 (설치 후 첫 실행 시 복원)
  | 'unknown';   // 알 수 없음

When the app is launched via a deep link from a fully terminated state:

// SDK 초기화 후 Pending 딥링크 확인
await AdStage.initialize({ apiKey: 'your-api-key' });
 
// 리스너 먼저 설정
AdStage.deepLink.setListener((data) => {
  console.log('딥링크:', data);
  handleDeepLink(data);
});
 
// Pending 딥링크 확인 및 처리
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' });
      
      // 딥링크 리스너 설정
      AdStage.deepLink.setListener((data) => {
        const { parameters } = data;
        
        // React Navigation으로 화면 이동
        if (navigationRef.current) {
          if (parameters?.screen) {
            navigationRef.current.navigate(parameters.screen, parameters);
          }
        }
      });
      
      // Cold start 딥링크 확인
      AdStage.deepLink.checkPendingDeepLink();
    };
    
    initSDK();
  }, []);
  
  return (
    <NavigationContainer ref={navigationRef}>
      {/* 네비게이션 스택 */}
    </NavigationContainer>
  );
}

import { AdStage } from '@adstage/react-native-sdk';
 
const createDeepLink = async () => {
  try {
    const result = await AdStage.deepLink.create({
      name: '여름 프로모션 링크',
      description: '2025년 여름 할인 이벤트',
      
      // 어트리뷰션 파라미터
      channel: 'instagram',
      subChannel: 'social',
      campaign: 'summer_sale_2025',
      
      // 리다이렉트 설정
      redirectConfig: {
        type: 'APP', // 'STORE' | 'APP' | 'WEB'
        android: {
          packageName: 'com.yourapp.android',
          appScheme: 'yourapp://promo',
          webUrl: 'https://yourapp.com/promo',
        },
        ios: {
          appStoreId: '1234567890',
          appScheme: 'yourapp://promo',
          webUrl: 'https://yourapp.com/promo',
        },
        desktop: {
          webUrl: 'https://yourapp.com/promo',
        },
      },
      
      // 커스텀 파라미터
      parameters: {
        promo_code: 'SUMMER25',
        discount: '20',
        screen: 'PromoDetail',
      },
    });
    
    console.log('✅ 딥링크 생성 성공');
    console.log('  - Short URL:', result.shortUrl);
    console.log('  - Short Path:', result.shortPath);
    console.log('  - Link ID:', result.linkId);
    
    // 링크 공유
    shareDeepLink(result.shortUrl);
    
  } catch (error) {
    console.error('❌ 딥링크 생성 실패:', error);
  }
};

CreateDeepLinkRequest Parameters

ParameterTypeRequiredDescription
namestringDeep link name
descriptionstring-Deep link description
shortPathstring-Custom Short Path
channelstring-Channel
subChannelstring-Sub channel
campaignstring-Campaign
adGroupstring-Ad group
creativestring-Ad creative
contentstring-Content
keywordstring-Keyword
redirectConfigRedirectConfig-Redirect configuration
parametersobject-Custom parameters

RedirectConfig Structure

interface RedirectConfig {
  type: 'STORE' | 'APP' | 'WEB';   // 리다이렉트 타입
  android?: PlatformConfig;          // Android 플랫폼 설정
  ios?: PlatformConfig;              // iOS 플랫폼 설정
  desktop?: { webUrl?: string };     // 데스크톱 설정
}
 
interface PlatformConfig {
  storeUrl?: string;     // 앱 스토어 URL
  appScheme?: string;    // 앱 커스텀 스킴
  webUrl?: string;       // 웹 폴백 URL
  packageName?: string;  // 패키지명(Android) / 번들 ID(iOS)
  appStoreId?: string;   // App Store ID (iOS)
}
TypeApp InstalledApp Not Installed
STOREGo to storeGo to store
APPLaunch app (real-time deep link)Go to store → deferred deep link after install
WEBGo to web URLGo to web URL

Utility Functions

Extracting Short Path

// URL에서 Short Path 추출
const shortPath = AdStage.deepLink.extractShortPath('https://adstage.net/ABCDEF');
console.log(shortPath); // "ABCDEF"

Manual Android Intent Handling

import { Linking } from 'react-native';
 
// Android에서 앱이 이미 실행 중일 때 새 Intent 처리
Linking.addEventListener('url', ({ url }) => {
  // 네이티브에서 자동 처리되지만, 필요시 수동 호출
  AdStage.deepLink.handleIntent();
});

Troubleshooting

iOS Issues

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

The ATT permission popup is not displayed

  • Verify that the NSUserTrackingUsageDescription key exists in Info.plist

Android Issues

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

Common Issues

  1. Verify that the listener was set up after SDK initialization completed
  2. Verify whether checkPendingDeepLink() was called
  3. Verify the network connection status

Parameters are not passed

  • Verify that values were correctly set in the parameters object when creating the deep link

Table of Contents