StageUp
Mobile SDKDeep Link

Unity

AdStage DeepLink Integration Guide (Unity)

Table of Contents

  1. Overview
  2. Project Setup
  3. Unity Project Configuration
  4. Android Platform Setup
  5. iOS Platform Setup
  6. Creating Deep Links
  7. Troubleshooting

Overview

The AdStage DeepLink SDK for Unity 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: Generate trackable links via server API
  • Attribution Tracking: Marketing analysis based on UTM parameters
  • Cross-Platform: Unified 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 → App launch → Restoration

Project Setup

1. Package Installation

Install via Package Manager:

Window → Package Manager → + → Add package from git URL
https://github.com/nbase-io/NBase-SDK-Unity.git?path=/AdStageSDK-Package

Or edit manifest.json directly:

{
  "dependencies": {
    "com.nbase.adstage": "https://github.com/nbase-io/NBase-SDK-Unity.git?path=/AdStageSDK-Package#3.0.0"
  }
}

2. Required Dependencies

Automatically included with package installation:

  • Newtonsoft.Json: JSON serialization
  • Native Plugins: iOS/Android bridge

Unity Project Configuration

1. SDK Initialization

using AdStageSDK;
using UnityEngine;
 
public class AdStageInitializer : MonoBehaviour
{
    void Start()
    {
        // Initialize AdStage
        AdStage.Initialize(
            apiKey: "your-api-key-here"
        );
        
        // Register deep link listener
        SetupDeepLinkListener();
        
        Debug.Log("✅ AdStage SDK Initialized");
    }
    
    private void SetupDeepLinkListener()
    {
        AdStage.SetDeepLinkListener(
            onDeepLink: (data) => {
                Debug.Log($"✅ Deep Link Received: {data.shortPath}");
                if (data.parameters != null && data.parameters.Count > 0)
                {
                    foreach (var param in data.parameters)
                    {
                        Debug.Log($"  - {param.Key}: {param.Value}");
                    }
                }
            },
            onError: (error) => {
                Debug.LogError($"❌ Deep Link Failed: {error}");
            }
        );
    }
}

Android Platform Setup

1. AndroidManifest.xml Configuration

Create Assets/Plugins/Android/AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name">
        
        <activity
            android:name="com.unity3d.player.UnityPlayerActivity"
            android:theme="@style/UnityThemeSelector"
            android:screenOrientation="fullSensor"
            android:launchMode="singleTask"
            android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
            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>
        </activity>
        
    </application>
</manifest>

2. launchMode Important Notes

android:launchMode="singleTask"
  • singleTask: Reuse existing Activity (recommended)
  • ⚠️ singleTop: Reuse only when at top of stack
  • standard: Create new instance every time (causes duplicate deep links)

iOS Platform Setup

1. Configure Info.plist in Xcode

<!-- URL Scheme Configuration -->
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>com.example.myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>

public async void CreateDetailedDeepLink()
{
    try
    {
         var builder = new DeepLinkBuilder("Unity Test Link")
             .SetDescription("Unity SDK Test")
             .SetCampaign("unity_test")
             .SetChannel("app_channel")
             .SetRedirectType(RedirectType.APP)
             .SetAndroidConfig("your.package.name","your_aos_scheme", "https://yourdomain.com")
             .SetIOSConfig("your.appstoreid", "your_ios_scheme", "https://yourdomain.com")
             .SetWebConfig("https://yourdomain.com")
             .AddParameter("user_id", "value")
             .AddParameter("test", "true");
        
         AdStage.CreateDeeplink(
             builder: builder,
             onSuccess: (shortUrl) =>
             {
                 InAppLog("✅ Deep Link Created Successfully!");
                 InAppLog($"  URL: {shortUrl}");
             },
             onError: (error) =>
             {
                 InAppLog($"❌ Deep Link Creation Failed: {error}");
             }
         );
        
    }
    catch (System.Exception e)
    {
        Debug.LogError($"❌ Error: {e.Message}");
    }
}

RedirectType Explanation

public enum RedirectType
{
    Store,  // Redirect to store
    
    App,    // App not installed → Redirect to store
            // App installed → Launch app (real-time deep link)
    
    Web     // Always redirect to web URL
            // Regardless of app installation
}

Android Deep Link Flow:

User clicks deep link (myapp://abc123)

Android System creates Intent

UnityPlayerActivity starts/resumes

AdStageLifecyclePlugin.onActivityCreated/Resumed()

AdStageUnityWrapper.handleIntent(intent)

AdStage.handleIntent(context, intent)

DeeplinkHandler.handleIntent()
  ├─ Extract URI: myapp://abc123
  ├─ Parse shortPath: abc123
  ├─ API call: GET /deeplinks/abc123
  └─ DeeplinkListener.onDeeplinkReceived()

UnitySendMessage("AdStageCallbackReceiver", "OnDeepLinkReceived", json)

AdStageCallbackReceiver.OnDeepLinkReceived(json)

User callback invoked

iOS Deep Link Flow:

User clicks deep link (myapp://abc123)

iOS System delivers URL

AdStageUnityAppController.application:openURL: or
AdStageUnityAppController.application:continueUserActivity:

AdStageUnityBridge.AdStageIOS_HandleDeepLink(url)

AdStageManager.shared.handleDeepLink(url)

DeepLinkManager.handleDeepLink()
  ├─ Parse URL
  ├─ API call
  └─ DeepLinkDelegate.onDeepLinkReceived()

UnitySendMessage("AdStageCallbackReceiver", "OnDeepLinkReceived", json)

AdStageCallbackReceiver.OnDeepLinkReceived(json)

User callback invoked

Troubleshooting

This is normal behavior! Editor only outputs logs.

Testing Method:

#if UNITY_EDITOR
[MenuItem("AdStage/Test DeepLink")]
public static void TestDeepLink()
{
    var testData = new DeepLinkData
    {
        ShortPath = "TEST123",
        Source = DeepLinkSource.Realtime,
        Parameters = new Dictionary<string, string>
        {
            { "promo", "EDITOR_TEST" }
        }
    };
    
    AdStage.SimulateDeepLink(testData);
}
#endif

Checklist:

  1. Check AndroidManifest.xml

    • Verify android:launchMode="singleTask" setting
    • Verify Intent Filter is correct
  2. Check adb logcat

adb logcat | grep -i adstage
  1. Test Intent
# Test URL Scheme
adb shell am start -W -a android.intent.action.VIEW -d "myapp://promo/summer" com.example.myapp
 
# Test HTTPS App Link
adb shell am start -W -a android.intent.action.VIEW -d "https://go.myapp.com/abc123" com.example.myapp

Checklist:

  1. Check Info.plist

    • CFBundleURLTypes configuration
    • Associated Domains configuration
  2. Verify Universal Links

# Check Apple App Site Association file
curl https://go.myapp.com/.well-known/apple-app-site-association
  1. Check Xcode Console
    • Check error messages
    • Check deep link reception logs

Android:

# Check Install Referrer
adb shell dumpsys package com.example.myapp | grep -i referrer

Solution:

  1. Add permission to AndroidManifest.xml:
<uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" />
  1. Configure Install Referrer in Play Console

Cause: Using launchMode="standard"

Solution:

<!-- AndroidManifest.xml -->
android:launchMode="singleTask"

6. Unity 2021+ IL2CPP Build Error

Issue:

TypeLoadException: Could not load type 'AdStageSDK.DeepLinkData'

Solution: Create link.xml file

Assets/link.xml:

<linker>
    <assembly fullname="AdStageSDK" preserve="all"/>
    <assembly fullname="AdStageSDK.Models" preserve="all"/>
    <assembly fullname="Newtonsoft.Json" preserve="all"/>
</linker>

7. JSON Serialization Error

Issue:

JsonSerializationException: Error converting value

Solution:

// Use Dictionary<string, object> for Parameters
var parameters = new Dictionary<string, object>
{
    { "key1", "value1" },
    { "key2", 123 },  // int also supported
    { "key3", true }  // bool also supported
};

Verification Method:

# Verify Digital Asset Links
curl https://go.myapp.com/.well-known/assetlinks.json

Checklist:

  • ✅ Using HTTPS
  • ✅ Content-Type: application/json
  • ✅ Verify SHA256 fingerprint is correct
  • ✅ Package name matches

FAQ

Q: Can I test deep links in Unity Editor?
A: In Editor, you can simulate using the AdStage.SimulateDeepLink() method.

Q: Can I use the same deep link URL for Android and iOS?
A: Yes, both URL Scheme and HTTPS links can be used identically.

Q: How do deferred deep links work?
A: App install → First launch → Query Install Referrer → Restore saved deep link → Invoke callback

Q: Is there a limit to the number of deep link parameters?
A: No server limit, but consider URL length limitation (~2000 characters).

Q: Do deep links work offline?
A: Real-time deep links require network. Deferred deep links are cached and restored even offline.

Q: Does it work with Unity WebGL?
A: WebGL cannot use native plugins, so it's not supported. In web environments, parse URL parameters directly.


Support

Contact:


© 2025 NBase. All rights reserved.

Table of Contents