Deferred Deep Links: Complete Guide

Learn how deferred deep links work and implement them on iOS & Android with the official SDDL SDKs. No hosting required — SDDL automatically publishes apple-app-site-association and assetlinks.json from your app settings.

What are Deferred Deep Links?

Deferred deep links send users to a specific in-app destination even if the app isn’t installed yet. The user taps a smart link, installs the app, and on first launch lands on the intended screen with preserved context (campaign, referrer, product ID, etc.).

How deferred linking works

  1. User clicks an SDDL link (from ads, email, SMS, social, etc.).
  2. SDDL stores the intent and keeps context across install.
  3. If the app isn’t present, the user is sent to the App Store / Play Store.
  4. On first launch, the app asks SDDL to resolve the payload and routes the user to the right screen.

iOS setup (Universal Links)

Enable Associated Domains in Xcode and add:

applinks:{YOUR_ID}.sddl.me
                        OR
applinks:{your.custom.domain}

You do not host apple-app-site-association yourself — SDDL publishes it automatically from your App settings.

SwiftUI integration (official SDDL iOS SDK)

import SwiftUI
import SDDLSDK

struct ContentView: View {
    var body: some View {
        Color.clear
            // 1) Universal Link delivered directly
            .onOpenURL { url in
                SDDLHelper.resolve(url,
                                   onSuccess: handlePayload(_:),
                                   onError: handleError(_:))
            }
            // 2) Universal Link via NSUserActivity
            .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in
                SDDLHelper.resolve(activity.webpageURL,
                                   onSuccess: handlePayload(_:),
                                   onError: handleError(_:))
            }
            // 3) Cold start (no URL at launch)
            .onAppear {
                SDDLHelper.resolve(nil,
                                   onSuccess: handlePayload(_:),
                                   onError: handleError(_:),
                                   readClipboard: false)
            }
    }
}

private func handlePayload(_ payload: [String: Any]) {
    // Navigate based on payload (e.g., product_id)
}

private func handleError(_ error: String) {
    // Optional: log or show a non-blocking message
}

Add the CocoaPods dependency in your Podfile:

platform :ios, '13.0'
use_frameworks!

target 'YourApp' do
  pod 'SDDLSDK', '~> 2.0.7' # use the latest version
end

Android setup (App Links)

SDDL auto-publishes assetlinks.json based on your App Links configuration (package name and SHA256 fingerprints) in the SDDL dashboard. In your project, add the repository and dependency:

// settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
        maven("https://jitpack.io")
    }
}
// app/build.gradle.kts
dependencies {
    implementation("com.github.nonanerz:sddl-android-sdk:2.0.9") // use the latest version
}

Declare App Links in your AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTop">

    <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="{YOUR_ID}.sddl.me OR {your.custom.domain}"
            android:pathPrefix="/" />
    </intent-filter>

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

</activity>

Using the SDDL Android SDK

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        SDDLHelper.resolve(this, intent, ::routeWith, ::handleDeepLinkError, readClipboard = false)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        SDDLHelper.resolve(this, intent, ::routeWith, ::handleDeepLinkError, readClipboard = false)
    }

    private fun routeWith(payload: JsonObject) {
        // navigate based on payload (e.g., product_id)
    }

    private fun handleDeepLinkError(error: String) {
        // optional: log/report
    }
}

Getting SHA256 fingerprints

# Debug
keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android

# Release
keytool -list -v -alias YOUR_ALIAS -keystore /path/to/your-release-key.jks

Add both Debug and Release fingerprints in SDDL → App Links Configuration.

Testing & Troubleshooting

  • Domains: use {YOUR_ID}.sddl.me or your verified custom domain connected in SDDL.
  • iOS: Associated Domains must be verified (no warnings in Xcode). After changing entitlements, delete and reinstall the app to refresh Universal Links.
  • AASA: SDDL publishes it automatically; ensure it’s reachable and not served with redirects. (No manual hosting needed.)
  • Android: set android:autoVerify="true". Verify with adb shell pm verify-app-links your.package.name and ensure fingerprints in SDDL match your build.
  • Cold start: call SDDLHelper.resolve(nil) (iOS) or SDDLHelper.resolve(this, intent) (Android) on launch so deferred context is restored.

FAQ

Do I need to host AASA or assetlinks.json?

No. SDDL automatically publishes both based on your app settings.

Is the SDK required?

The SDKs are the easiest way to integrate and restore context, but a pure REST flow is possible if you prefer.

Related guides

Continue exploring