Adding Deep Linking Support
Deep links direct users to specific content within your app. When users tap a deep link on a device with your app installed, the app opens directly to the intended content, such as a product page or specific experience.
Singular tracking links support both standard deep linking (for installed apps) and deferred deep linking (for new installs). For comprehensive information, see the Deep Linking FAQ and Singular Links FAQ .
Requirements
Prerequisites
Complete the Singular Links Prerequisites to enable deep linking for your app.
Implement Singular Links Handler
The SingularLinkHandler provides a callback mechanism to retrieve deep link, deferred deep link, and passthrough parameters from Singular tracking links when the app opens.
Available Parameters:
- Deep Link (_dl): The destination URL within your app for users clicking the link
- Deferred Deep Link (_ddl): The destination URL for users who install the app after clicking the link
- Passthrough (_p): Custom data passed through the tracking link for additional context
SDK Configuration
Configure AndroidManifest
Before the SDK can resolve a deep link, your activity must declare an intent filter so Android routes incoming
ACTION_VIEW
intents to it. Add the filter to the launcher activity (or whichever activity should receive deep links) in
AndroidManifest.xml
and substitute your own scheme and host.
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Custom URI 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="yourapp" />
</intent-filter>
<!-- HTTPS Singular Link (replace with your branded subdomain) -->
<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="http" />
<data android:scheme="https" />
<data android:host="yourcompany.sng.link" />
<data android:pathPrefix="/A"/>
<data android:pathPrefix="/B"/>
<data android:pathPrefix="/E"/>
<data android:pathPrefix="/F"/>
</intent-filter>
</activity>
Without these intent filters the OS will not deliver the deep link intent to your activity and the
SingularLinkHandler
callback will not fire. App Links (HTTPS scheme with
android:autoVerify="true"
) also require a hosted
/.well-known/assetlinks.json
file on the destination domain.
Add SingularLinkHandler to Config
Configure the SingularLinkHandler during SDK initialization to process incoming deep link and deferred deep link data.
private fun initSingularSDK() {
val config = SingularConfig("SDK_KEY", "SDK_SECRET")
.withSingularLink(intent) { params ->
val deeplink = params.deeplink
val passthrough = params.passthrough
val isDeferred = params.isDeferred
Log.d("SingularLink", "Deeplink: ${deeplink ?: "null"}")
Log.d("SingularLink", "Passthrough: ${passthrough ?: "null"}")
Log.d("SingularLink", "Is Deferred: $isDeferred")
// Handle deep link routing
deeplink?.let { url ->
handleDeepLink(url, isDeferred)
}
}
try {
Singular.init(applicationContext, config)
Log.d("Singular", "SDK initialized successfully")
} catch (e: Exception) {
Log.e("Singular", "SDK initialization failed: ${e.message}")
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// Configure onNewIntent to handle Warm start deeplinks
intent?.let {
setIntent(intent)
initSingularSDK()
}
}
private void initSingularSDK() {
SingularConfig config = new SingularConfig("SDK_KEY", "SDK_SECRET")
.withSingularLink(getIntent(), new SingularLinkHandler() {
@Override
public void onResolved(SingularLinkParams params) {
String deeplink = params.getDeeplink();
String passthrough = params.getPassthrough();
boolean isDeferred = params.isDeferred();
Log.d("SingularLink", "Deeplink: " + (deeplink != null ? deeplink : "null"));
Log.d("SingularLink", "Passthrough: " + (passthrough != null ? passthrough : "null"));
Log.d("SingularLink", "Is Deferred: " + isDeferred);
// Handle deep link routing
if (deeplink != null) {
handleDeepLink(deeplink, isDeferred);
}
}
});
try {
Singular.init(getApplicationContext(), config);
Log.d("Singular", "SDK initialized successfully");
} catch (Exception e) {
Log.e("Singular", "SDK initialization failed: " + e.getMessage());
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// Configure onNewIntent to handle Warm start deeplinks
if (intent != null) {
setIntent(intent);
initSingularSDK();
}
}
Note: The SingularLinkHandler is triggered only when the app opens through a Singular Link. For more information, see the Singular Links FAQ .
withSingularLink
defaults to a 10-second short link resolution timeout when called without the
shortlinkTimeoutSec
argument. To override it, use the three-argument overload
withSingularLink(Intent intent, SingularLinkHandler handler, long shortlinkTimeoutSec)
. When the intent action is
ACTION_VIEW
, the SDK also marks the session as opened with a deep link, which affects attribution.
Handler Behavior
Understanding SingularLinkHandler Execution
The SingularLinkHandler behaves differently depending on whether the app is freshly installed or already installed.
Fresh Install (Deferred Deep Link)
On a fresh install, no Open URL exists when the app launches. Singular completes attribution to determine if the tracking link contained a deep link or deferred deep link value.
Deferred Deep Link Flow:
- User clicks a Singular tracking link configured with a deep link value
- User installs and opens the app for the first time
- Singular SDK sends the first session to Singular servers
- Attribution completes and identifies the deep link from the tracking link
-
Deep link value returns to the SingularLinkHandler in the
deeplinkparameter withisDeferred = true
Testing Deferred Deep Links:
- Uninstall the app from the test device (if currently installed)
- Reset your Google Advertising ID (GAID) on the device
- Click the Singular tracking link from the device (ensure it's configured with a deep link value)
- Install and open the app
Attribution should complete successfully, and the deferred deep link value will be passed to the SingularLinkHandler.
Pro Tip:
When testing deep links with a development build using a different package name (e.g.,
com.example.dev
instead of
com.example.prod
), configure the tracking link specifically for the development app's package name. After clicking the test link, install the development build directly onto the device via Android Studio or APK, rather than downloading the production app from the app store.
Already Installed (Immediate Deep Link)
When the app is already installed, clicking a Singular Link opens the app immediately using Android App Links technology.
Immediate Deep Link Flow:
- User clicks a Singular tracking link
- Android OS provides an Open URL containing the entire Singular tracking link
- During SDK initialization, Singular parses the Android Intent
-
Singular extracts the
deeplinkandpassthroughvalues -
Values return through the SingularLinkHandler with
isDeferred = false
Passthrough Parameters
Capture additional data from the tracking link click using passthrough parameters.
If a
passthrough (_p)
parameter is included in the tracking link, the SingularLinkHandler's
passthrough
parameter contains the corresponding data. Use this for capturing campaign metadata, user segmentation data, or any custom information you need in the app.
val config = SingularConfig("SDK_KEY", "SDK_SECRET")
.withSingularLink(intent) { params ->
// Extract passthrough data
params.passthrough?.let { passthroughData ->
try {
// Parse and use custom data
val jsonData = JSONObject(passthroughData)
val campaignId = jsonData.optString("campaign_id")
val userSegment = jsonData.optString("segment")
Log.d("SingularLink", "Campaign ID: $campaignId")
Log.d("SingularLink", "User Segment: $userSegment")
} catch (e: JSONException) {
Log.e("SingularLink", "Error parsing passthrough data", e)
}
}
}
SingularConfig config = new SingularConfig("SDK_KEY", "SDK_SECRET")
.withSingularLink(getIntent(), new SingularLinkHandler() {
@Override
public void onResolved(SingularLinkParams params) {
// Extract passthrough data
String passthroughData = params.getPassthrough();
if (passthroughData != null) {
try {
// Parse and use custom data
JSONObject jsonData = new JSONObject(passthroughData);
String campaignId = jsonData.optString("campaign_id");
String userSegment = jsonData.optString("segment");
Log.d("SingularLink", "Campaign ID: " + campaignId);
Log.d("SingularLink", "User Segment: " + userSegment);
} catch (JSONException e) {
Log.e("SingularLink", "Error parsing passthrough data");
}
}
}
});
Forward All Query Parameters
Capture all query parameters from the tracking link URL by appending the
_forward_params=2
parameter.
When
_forward_params=2
is added to the tracking link, all query parameters are included in the
deeplink
parameter of the SingularLinkHandler, giving you access to the complete URL with all its parameters.
Example Tracking Link:
https://yourapp.sng.link/A1b2c/abc123?_dl=myapp://product/123&_forward_params=2&utm_source=facebook&promo=SALE2024
The SingularLinkHandler will receive:
deeplink = "myapp://product/123?utm_source=facebook&promo=SALE2024"