Skip to main content

Deep Link

  • Redirect users to specific in-app content via URI.
  • Android 6.0+ (API level 23) supports Android App Links (web links with auto verification). With the autoVerify attribute, the system will automatically verify whether the app matches the host domain specified in the intent-filter.
  • Players can manually change system settings to select the default app to handle this type of link.

Step 1: Configure SDK

Add intent-filter in AndroidManifest.xml:

  • scheme: Protocol type (e.g., lipass, http, https)
  • host: Domain (e.g., app, www.levelinfinite.com)
  • pathPrefix (optional): Page path prefix, used to distinguish multi-store packages

Custom Protocol:

<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="http"
android:host="sdk-deeplink-confs.intlgame.com"
android:pathPrefix="/channelA" />
</intent-filter>

HTTP/HTTPS Web 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="https"
android:host="www.levelinfinite.com"
android:pathPrefix="/app" />
</intent-filter>

Android App Link (Android 6.0+, with auto verification):

<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="www.levelinfinite.com"
android:pathPrefix="/app" />
</intent-filter>

Handling Intent Data:

Obtain deep link data in onCreate and onNewIntent of MainActivity.

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Intent intent = getIntent();
Uri data = intent.getData();

if (data != null) {
INTLSDK.Deeplink.receive(data.toString());
}
}

public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
INTLSDK.onNewIntent(intent);

Uri appLinkData = intent.getData();
if (appLinkData != null) {
INTLSDK.Deeplink.receive(appLinkData.toString());
}
}

Deploy assetlinks.json file to:

https://domain.name/.well-known/assetlinks.json

File format:

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example",
"sha256_cert_fingerprints": ["Your_SHA256_fingerprint"]
}
}]

Generate SHA-256 fingerprint:

keytool -list -v -keystore my-release-key.keystore

Multi-store channel package configuration (optional)

  • Channel package: different APKs for the same app, with different package names for different distribution channels.
  • pathPrefix: distinguishes channels to ensure the correct APK is opened when clicking a deep link.

Configuration example:

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.puppies.app",
"sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.monkeys.app",
"sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]

Step 3: Testing

  • Click the link in an Android text editor (such as Notes).
  • Check if the logs contain: [ (intl_deeplink_manager.cpp:37) Receive]
android-deeplink
  • Use adb to test link redirection:
  adb shell am start -W -a android.intent.action.VIEW -d "lipass://app/xxxxxx" com.example.android_app
note

调用网页 URI 时,Android 系统会依序尝试执行下列操作,直到请求成功为止:

  1. If you have already specified the default app to handle this type of link, the app will open.
  2. Open the only app that can handle this type of link.
  3. Allow players to select an app to open from the dialog.

WeChat Limitation: Links in the WeChat built-in browser will not redirect to the app


Step 1: Configure Web Server

Deploy the apple-app-site-association file to the root directory of the web server. Path:

https://domain.name/.well-known/apple-app-site-association

{
"applinks": {
"apps": [],
"details": [{
"appID": "TEAMID.com.your.bundleid",
"paths": ["/app/*"]
}]
}
}

Key points:

  • apps must be an empty array.
  • appID = Team ID + Bundle ID
  • paths supports wildcards (* and ?)
  • Use "NOT " prefix to exclude paths (e.g., "NOT /videos/wwdc/2010/*")
  • Path strings must be case-sensitive.
  • The system evaluates paths in the specified order and stops when a match is found.
  • Re-signed App: Must also add the appID and paths of the re-signed certificate to the configuration.

Path configuration examples:

  • Entire site: "*"
  • Specific link: "/wwdc/news/"
  • Specific part of site: "/videos/wwdc/2015/*"
  • Combined wildcards: "/foo/*/bar/201?/mypage"

Step 2: Enable Associated Domains

In your Apple Developer Account:

  • Enable the Associated Domains capability.
  • Regenerate the provisioning profile.
adp-associated-domain

In Xcode:

  • Add the Associated Domains capability.
  • Add the domain: applinks:your_link (no https:// required)
adp-associated-domain

Or configure via code:

Add the following code in PostProcess:

#if UNITY_2019_3_OR_NEWER
var capManager = new UnityEditor.iOS.Xcode.ProjectCapabilityManager(projPath, entitlementsFilePath, targetGuid: targetProjectName);
#else
var capManager = new UnityEditor.iOS.Xcode.ProjectCapabilityManager(projPath, entitlementsFilePath, targetProjectName);
#endif

capManager.AddAssociatedDomains(new string[] { "applinks:your_link" });

Step 3: Configure SDK

Due to the current INTLCore, the hook does not work on this entry method, so you should add the iOS lifecycle entry function in the UnityAppController.mm file of the UnityAppController class.

Currently, the XCodePostProcess.cs code already automatically adds the application:continueUserActivity:restorationHandler method during compiled post-processing (no additional code needed in the function).

note

Do not add lifecycle entry functions in subclasses of UnityAppController.Ensure that the entry function is in the same class as system lifecycle functions such as application:openURL:options:.

- (BOOL)application:(UIApplication *)application 
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {
return YES;
}

Step 4: Testing

  • Click the link in iOS Notes.
  • The app should open and log: INTLApplicationDelegate:continueUserActivity
note

若配置正确但链接不生效:尝试重启设备或重新安装应用

First time opening the app after installation or update:

  1. App sends a GET request to the configured domain to fetch the apple-app-site-association file.
  2. App registers the file with the system.
  3. WebView checks URLs against the registered configuration when opening URLs.
  4. If a universal link is registered, the app opens and triggers delegate.
  5. If not matched, WebView continues to redirect the URL.

WeChat Limitation: Links in the WeChat built-in browser will not redirect to the app

Share Link

Cross-platform attribution link, supports deep linking and deferred deep linking.

Feature Description:

  • Deep linking: App is installed, directly open the specific location in app.
  • Deferred deep linking: App is not installed, redirect to app store for download. After installation, open and redirect to the specific location.

Step 1: Create App in AppsFlyer Console

Create your app following the AppsFlyer documentation Adding an app to AppsFlyer.

Share Link Share Link
  • Android: Add SHA-256 fingerprint in "Use App Links", and save the generated intent-filter code.
Share Link

Step 3: Configure SDK

Android:

  1. Add in INTLConfig.ini:
   APPSFLYER_APP_INVITE_ONELINK_ID_ANDROID = {Template_ID}
  1. Add AppsFlyer to [Android LifeCycle] (if missing):
   LIFECYCLE = WeChat,QQ,Twitter,Adjust,Facebook,Google,Line,VK,Garena,Discord,Dmm,Update,Firebase,WhatsApp,Permission,GooglePGS,AppsFlyer
  1. Add intent-filter in the game's MainActivity in AndroidManifest.xml to open the game, and replace {Subdomain} and {OneLink_Template_ID}.
    <activity
android:name="com.intlgame.unity.MainActivity"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask">
<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:host="{Subdomain}.onelink.me"
android:pathPrefix="/{OneLink_Template_ID}"
android:scheme="https" />
</intent-filter>
</activity>

iOS:

  1. Add in INTLConfig.ini:
   APPSFLYER_APP_INVITE_ONELINK_ID_IOS = {Template_ID}
  1. Add subdomain to Associated Domains:
    • Unity: Modify XCodePostProcess.cs, add
    capManager.AddAssociatedDomains(new string[] { "appslinks:subdomain.onelink.me"});
    • UE: Add subdomain in AssociatedDomains of DefaultEngine.ini:
    AssociatedDomains=appslinks:subdomain.onelink.me

Set OneLink callback:

INTLAPI.AddExtendResultObserver(OnExtendEvent); // Set callback

public void OnExtendEvent(INTLExtendResult extendResult)
{
if ("OnOneLinkResult".Equals(extendResult.ExtendMethodName)) // OneLink click callback
{
if (extendResult.RetCode == 0) // OneLink click callback succeeded
{
string jsonParameter = extendResult.ExtraJson; // Parameters returned by AF after clicking OneLink
// TODO: Handle OneLink Parameter
}
else // OneLink click callback failed
{
// TODO: handle OneLink Failed result
}
}
}

Set the callback for the user clicking the shared OneLink:

The generation and clicking of OneLink share a callback. They can be distinguished by parameters in INTLExtendResult.

    INTLAPI.AddExtendResultObserver(OnExtendEvent);//Setup callback
public void OnExtendEvent(INTLExtendResult extendResult)
{
if ("OnOneLinkResult".Equals(extendResult.ExtendMethodName))//OneLink click callback
{
if (extendResult.RetCode == 0)//OneLink click callback successful
{
string jsonParameter = extendResult.ExtraJson;//Parameters returned by AF after clicking OneLink
//TODO:Handle OneLink Parameter
}
else//OneLink click callback failed
{
//TODO: handle OneLink Failed result
}
}
}

Refer to the sample code to generate OneLink, and modify parameters as needed.:

StringBuilder sb = new StringBuilder();
sb.Append("{");
sb.Append("\"deep_link_value\":\"abc\"").Append(","); //<TARGET_VIEW>

sb.Append("\"deep_link_sub1\":\"1234\"").Append(",");//<PROMO_CODE>
sb.Append("\"deep_link_sub2\":\"1234\"").Append(",");//<REFERRER_ID(openid)>
sb.Append("\"channel\":\"mobile_share\"").Append(",");//Channel
sb.Append("\"campaign\":\"summer_sale\"");//Campaign
//Other parameters, optional
sb.Append("\"deep_link_sub3\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub4\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub5\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub6\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub7\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub8\":\"1234\"").Append(",");

sb.Append("\"af_sub4\":\"12324\"").Append(",");
sb.Append("\"af_sub5\":\"dfasdf\"").Append(",");

sb.Append("}");
String paramsJsonString = sb.ToString();
INTLExtend.Invoke("AppsFlyer", "generateInviteLinkUrl", paramsJsonString);

Step 5: Test Integration

  1. Enter the AppsFlyer console.
  2. In the left sidebar, click SDK Integration Tests.
  3. Select the application to test and click Run test.
Share Link
  1. On the Run non-organic install test page, select the test device and choose Other.
  2. Scan the QR code and install the app.
Share Link
note

To test activation again, remove the application and restart the test.


Client Interface

APIFunction Definition
SetDeepLinkObserverSet a deep link callback to notify the game engine layer whether data has arrived. Upon receiving a callback, manually call the Fetch function to retrieve the data.
RemoveDeepLinkObserverRemove the deep link callback.

Fetch

Fetch interface retrieves cached deep link data from the SDK.For more information, see:

APIFunction Definition
FetchRetrieve cached deep link data from the SDK.