{"id":361,"date":"2026-03-05T09:00:00","date_gmt":"2026-03-05T14:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=361"},"modified":"2026-03-07T04:33:09","modified_gmt":"2026-03-07T09:33:09","slug":"complete-guide-deep-linking-2026","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/complete-guide-deep-linking-2026\/","title":{"rendered":"The Complete Guide to Deep Linking in 2026"},"content":{"rendered":"\n<p>Deep links are URLs that send users directly to specific content inside your mobile app. Instead of landing on your app&#39;s home screen, users arrive exactly where you want them: a product page, user profile, checkout flow, or any other screen in your app.<\/p>\n\n\n\n<p>This guide covers everything you need to know about implementing deep links in 2026. We&#39;ll explore the different types of deep links, walk through implementation on iOS and Android, examine common pitfalls, and share best practices from production apps handling millions of deep link clicks.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Deep Linking?<\/h2>\n\n\n\n<p>A deep link is a URL that points to a specific location within a mobile app rather than simply launching it. Think of deep links as the mobile equivalent of web URLs. Just as <code>https:\/\/example.com\/products\/shoes<\/code> takes you to a specific product page on a website, a deep link like <code>myapp:\/\/products\/shoes<\/code> or <code>https:\/\/example.com\/products\/shoes<\/code> can take you directly to that same product inside the mobile app.<\/p>\n\n\n\n<p>Deep linking solves a fundamental problem in mobile apps: without it, every external link to your app dumps users on the same generic home screen. Users then have to navigate through your app to find what they came for, assuming they remember what that was. Many won&#39;t bother.<\/p>\n\n\n\n<p>The technical implementation varies by platform. iOS uses Universal Links, Android uses App Links, and both platforms support custom URL schemes. Modern deep linking platforms like <a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku<\/a> handle these platform differences automatically, letting you focus on defining your link routes rather than wrestling with configuration files.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Types of Deep Links<\/h2>\n\n\n\n<p>There are three main types of deep links, each solving different problems. For a focused breakdown, see <a href=\"https:\/\/tolinku.com\/blog\/types-of-deep-links\/\">types of deep links: standard, deferred, and contextual<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Standard Deep Links<\/h3>\n\n\n\n<p>Standard deep links work when the app is already installed. They use either custom URL schemes (<code>myapp:\/\/path<\/code>) or HTTPS links that the operating system recognizes should open in your app.<\/p>\n\n\n\n<p><strong>Custom URL Schemes:<\/strong><\/p>\n\n\n\n<pre><code>myapp:\/\/product\/123\nmyapp:\/\/user\/johndoe\nmyapp:\/\/settings\/notifications\n<\/code><\/pre>\n\n\n\n<p><strong>HTTPS Deep Links (Universal Links\/App Links):<\/strong><\/p>\n\n\n\n<pre><code>https:\/\/myapp.com\/product\/123\nhttps:\/\/myapp.com\/user\/johndoe\nhttps:\/\/myapp.com\/settings\/notifications\n<\/code><\/pre>\n\n\n\n<p>Standard deep links fail when the app isn&#39;t installed. Custom URL schemes show an error. HTTPS links fall back to the web, which might be what you want, or might not.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Deferred Deep Links<\/h3>\n\n\n\n<p>Deferred deep links remember where the user wanted to go, survive the app installation process, and route them to the correct screen after they open the app for the first time. This solves the &quot;install gap&quot; problem.<\/p>\n\n\n\n<p>Here&#39;s how deferred deep linking works:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>User clicks a deep link on a device without your app<\/li>\n<li>System redirects to the App Store or Google Play<\/li>\n<li>User installs and opens the app<\/li>\n<li>App retrieves the original deep link destination<\/li>\n<li>User arrives at the intended screen, not the home screen<\/li>\n<\/ol>\n\n\n\n<p>The magic happens through device fingerprinting or deterministic matching. The deep linking service matches the click before install with the app open after install using device characteristics like IP address, user agent, screen resolution, and timezone.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Contextual Deep Links<\/h3>\n\n\n\n<p>Contextual deep links carry additional data beyond just the destination. This data persists through the installation process and provides context when the user arrives in the app.<\/p>\n\n\n\n<pre><code>https:\/\/myapp.com\/invite?referrer=alice&amp;campaign=summer2024&amp;discount=20\n<\/code><\/pre>\n\n\n\n<p>Contextual deep links enable powerful use cases:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Referral programs with attribution<\/li>\n<li>Personalized onboarding flows<\/li>\n<li>Campaign-specific promotions<\/li>\n<li>A\/B testing different user experiences<\/li>\n<li>Pre-filling forms with user data<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">How Deep Linking Works on iOS<\/h2>\n\n\n\n<p>iOS supports two deep linking mechanisms: custom URL schemes and Universal Links. Apple strongly recommends Universal Links for production apps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Custom URL Schemes on iOS<\/h3>\n\n\n\n<p>URL schemes are the older approach. You register a custom protocol in your app&#39;s Info.plist:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;key&gt;CFBundleURLTypes&lt;\/key&gt;\n&lt;array&gt;\n    &lt;dict&gt;\n        &lt;key&gt;CFBundleURLSchemes&lt;\/key&gt;\n        &lt;array&gt;\n            &lt;string&gt;myapp&lt;\/string&gt;\n        &lt;\/array&gt;\n    &lt;\/dict&gt;\n&lt;\/array&gt;\n<\/code><\/pre>\n\n\n\n<p>Then handle incoming URLs in your AppDelegate:<\/p>\n\n\n\n<pre><code class=\"language-swift\">func application(_ app: UIApplication, \n                 open url: URL, \n                 options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -&gt; Bool {\n    \/\/ Parse the URL and navigate to the appropriate screen\n    if url.scheme == &quot;myapp&quot; {\n        let path = url.path\n        let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems\n        \n        \/\/ Handle navigation based on path and query parameters\n        \/\/ Example: myapp:\/\/product\/123?source=email\n        if path.starts(with: &quot;\/product\/&quot;) {\n            let productId = String(path.dropFirst(&quot;\/product\/&quot;.count))\n            navigateToProduct(id: productId)\n        }\n    }\n    return true\n}\n<\/code><\/pre>\n\n\n\n<p>The problem with URL schemes: any app can claim any scheme. If two apps register <code>myapp:\/\/<\/code>, iOS picks one arbitrarily. There&#39;s no verification that the app claiming the scheme is legitimate. For a detailed comparison, see <a href=\"https:\/\/tolinku.com\/blog\/uri-schemes-vs-universal-links\/\">URI schemes vs Universal Links<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Universal Links on iOS<\/h3>\n\n\n\n<p>Universal Links solve the security and uniqueness problems of URL schemes. They&#39;re HTTPS URLs that open in your app when it&#39;s installed, or fall back to your website when it&#39;s not.<\/p>\n\n\n\n<p>For the full iOS deep dive, see <a href=\"https:\/\/tolinku.com\/blog\/universal-links-everything-you-need-to-know\/\">Universal Links: everything you need to know<\/a>. Setting up Universal Links requires:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>An apple-app-site-association (AASA) file hosted on your domain<\/li>\n<li>Associated Domains capability in your app<\/li>\n<li>Code to handle incoming Universal Links<\/li>\n<\/ol>\n\n\n\n<p><strong>Apple App Site Association file<\/strong> (hosted at <code>https:\/\/yourdomain.com\/.well-known\/apple-app-site-association<\/code>):<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;applinks&quot;: {\n    &quot;apps&quot;: [],\n    &quot;details&quot;: [\n      {\n        &quot;appID&quot;: &quot;TEAMID.com.company.app&quot;,\n        &quot;paths&quot;: [\n          &quot;\/product\/*&quot;,\n          &quot;\/user\/*&quot;,\n          &quot;\/invite\/*&quot;\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Xcode configuration<\/strong> (Associated Domains):<\/p>\n\n\n\n<pre><code>applinks:yourdomain.com\n<\/code><\/pre>\n\n\n\n<p><strong>Swift implementation<\/strong>:<\/p>\n\n\n\n<pre><code class=\"language-swift\">func application(_ application: UIApplication,\n                 continue userActivity: NSUserActivity,\n                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -&gt; Void) -&gt; Bool {\n    \n    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,\n          let url = userActivity.webpageURL else {\n        return false\n    }\n    \n    \/\/ Handle the Universal Link\n    handleDeepLink(url: url)\n    return true\n}\n\n\/\/ For iOS 13+ with SceneDelegate\nfunc scene(_ scene: UIScene, continue userActivity: NSUserActivity) {\n    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,\n          let url = userActivity.webpageURL else {\n        return\n    }\n    \n    handleDeepLink(url: url)\n}\n<\/code><\/pre>\n\n\n\n<p>Apple&#39;s <a href=\"https:\/\/developer.apple.com\/documentation\/xcode\/supporting-universal-links-in-your-app\" rel=\"nofollow noopener\" target=\"_blank\">Universal Links documentation<\/a> provides additional implementation details.<\/p>\n\n\n\n<p>The AASA file must be served over HTTPS with a valid certificate, return a 200 status code, and have the correct Content-Type header (<code>application\/json<\/code>). iOS downloads this file when the app is installed and periodically afterwards to check for updates. For setup details, see the <a href=\"https:\/\/tolinku.com\/blog\/aasa-file-setup\/\">AASA file setup guide<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Deep Linking Works on Android<\/h2>\n\n\n\n<p>Android also supports two deep linking mechanisms: custom URL schemes (via Intent Filters) and App Links (Android&#39;s equivalent to Universal Links).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Intent Filters on Android<\/h3>\n\n\n\n<p>Intent Filters are configured in your AndroidManifest.xml:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;activity android:name=&quot;.MainActivity&quot;&gt;\n    &lt;intent-filter&gt;\n        &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; \/&gt;\n        \n        &lt;!-- Custom URL scheme --&gt;\n        &lt;data android:scheme=&quot;myapp&quot; \/&gt;\n    &lt;\/intent-filter&gt;\n    \n    &lt;intent-filter&gt;\n        &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; \/&gt;\n        \n        &lt;!-- HTTPS URLs --&gt;\n        &lt;data android:scheme=&quot;https&quot; \n              android:host=&quot;myapp.com&quot; \n              android:pathPrefix=&quot;\/product&quot; \/&gt;\n    &lt;\/intent-filter&gt;\n&lt;\/activity&gt;\n<\/code><\/pre>\n\n\n\n<p>Handle incoming intents in your Activity:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_main)\n    \n    handleIntent(intent)\n}\n\noverride fun onNewIntent(intent: Intent) {\n    super.onNewIntent(intent)\n    handleIntent(intent)\n}\n\nprivate fun handleIntent(intent: Intent) {\n    val action = intent.action\n    val data = intent.data\n    \n    if (Intent.ACTION_VIEW == action &amp;&amp; data != null) {\n        val path = data.path\n        val productId = data.getQueryParameter(&quot;id&quot;)\n        \n        when {\n            path?.startsWith(&quot;\/product\/&quot;) == true -&gt; {\n                val id = path.removePrefix(&quot;\/product\/&quot;)\n                navigateToProduct(id)\n            }\n            path?.startsWith(&quot;\/user\/&quot;) == true -&gt; {\n                val username = path.removePrefix(&quot;\/user\/&quot;)\n                navigateToUser(username)\n            }\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Android App Links<\/h3>\n\n\n\n<p>App Links are verified HTTPS URLs that open directly in your app without showing the disambiguation dialog. For the full Android guide, see <a href=\"https:\/\/tolinku.com\/blog\/android-app-links-complete-guide\/\">Android App Links: the complete guide<\/a>. They require:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Digital Asset Links file hosted on your domain<\/li>\n<li>Intent filters with <code>autoVerify=&quot;true&quot;<\/code><\/li>\n<li>Android 6.0 (API level 23) or higher<\/li>\n<\/ol>\n\n\n\n<p><strong>Digital Asset Links file<\/strong> (hosted at <code>https:\/\/yourdomain.com\/.well-known\/assetlinks.json<\/code>):<\/p>\n\n\n\n<pre><code class=\"language-json\">[{\n  &quot;relation&quot;: [&quot;delegate_permission\/common.handle_all_urls&quot;],\n  &quot;target&quot;: {\n    &quot;namespace&quot;: &quot;android_app&quot;,\n    &quot;package_name&quot;: &quot;com.company.app&quot;,\n    &quot;sha256_cert_fingerprints&quot;:\n    [&quot;FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C&quot;]\n  }\n}]\n<\/code><\/pre>\n\n\n\n<p><strong>AndroidManifest.xml configuration<\/strong>:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;activity android:name=&quot;.MainActivity&quot;&gt;\n    &lt;intent-filter android:autoVerify=&quot;true&quot;&gt;\n        &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; \/&gt;\n        \n        &lt;data android:scheme=&quot;https&quot; \n              android:host=&quot;myapp.com&quot; \/&gt;\n    &lt;\/intent-filter&gt;\n&lt;\/activity&gt;\n<\/code><\/pre>\n\n\n\n<p>Google&#39;s <a href=\"https:\/\/developer.android.com\/training\/app-links\" rel=\"nofollow noopener\" target=\"_blank\">Android App Links documentation<\/a> covers the verification process in detail.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Implementing Deferred Deep Linking<\/h2>\n\n\n\n<p>Deferred deep linking requires tracking the user&#39;s intended destination before they install your app, then retrieving it after installation. You can build this yourself or use a deep linking platform. For a dedicated walkthrough, see <a href=\"https:\/\/tolinku.com\/blog\/deferred-deep-linking-how-it-works\/\">deferred deep linking: how it works<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Building Deferred Deep Linking Yourself<\/h3>\n\n\n\n<p>The basic approach uses device fingerprinting:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><p><strong>Before install<\/strong>: When a user clicks a deep link without your app installed, redirect them to a web page that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Collects device fingerprint (IP, user agent, screen size, timezone)<\/li>\n<li>Stores the fingerprint with the intended deep link destination<\/li>\n<li>Redirects to the App Store or Google Play<\/li>\n<\/ul>\n\n\n\n<p><strong>After install<\/strong>: When the app opens for the first time:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Collect the same device fingerprint<\/li>\n<li>Send it to your server<\/li>\n<li>Match against recent clicks<\/li>\n<li>Return the original deep link destination<\/li>\n<li>Navigate to the appropriate screen<\/li>\n<\/ul>\n\n\n\n<p>Here&#39;s a simplified server implementation:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Store click data\napp.post(&#39;\/api\/deeplink\/click&#39;, (req, res) =&gt; {\n  const { fingerprint, destination, timestamp } = req.body;\n  \n  \/\/ Store in database with TTL (e.g., 24 hours)\n  await db.clicks.create({\n    fingerprint: hashFingerprint(fingerprint),\n    destination,\n    timestamp,\n    expires: Date.now() + 24 * 60 * 60 * 1000\n  });\n  \n  res.json({ success: true });\n});\n\n\/\/ Retrieve destination after install\napp.post(&#39;\/api\/deeplink\/match&#39;, async (req, res) =&gt; {\n  const { fingerprint } = req.body;\n  const hashedFingerprint = hashFingerprint(fingerprint);\n  \n  \/\/ Find recent clicks matching this fingerprint\n  const match = await db.clicks.findOne({\n    fingerprint: hashedFingerprint,\n    expires: { $gt: Date.now() }\n  });\n  \n  if (match) {\n    \/\/ Delete the match to prevent reuse\n    await db.clicks.delete({ _id: match._id });\n    res.json({ destination: match.destination });\n  } else {\n    res.json({ destination: null });\n  }\n});\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Using a Deep Linking Platform<\/h3>\n\n\n\n<p>Building deferred deep linking yourself means handling:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Device fingerprinting algorithms<\/li>\n<li>Probabilistic matching with confidence scores<\/li>\n<li>Fallback mechanisms when matching fails<\/li>\n<li>Privacy compliance (GDPR, CCPA)<\/li>\n<li>Scale and performance optimization<\/li>\n<\/ul>\n\n\n\n<p>Platforms like <a href=\"https:\/\/tolinku.com\/docs\/concepts\/deep-linking\/\">Tolinku<\/a> handle this complexity for you. After SDK integration, deferred deep linking works automatically:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ iOS - AppDelegate or SceneDelegate\nTolinku.shared.handleDeepLink { result in\n    switch result {\n    case .success(let deepLink):\n        \/\/ Navigate to deepLink.url\n        \/\/ Access context data via deepLink.parameters\n    case .failure(let error):\n        \/\/ Handle error or default navigation\n    }\n}\n<\/code><\/pre>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ Android - MainActivity\nTolinku.handleDeepLink(this) { result -&gt;\n    when (result) {\n        is DeepLinkResult.Success -&gt; {\n            \/\/ Navigate to result.url\n            \/\/ Access context via result.parameters\n        }\n        is DeepLinkResult.Error -&gt; {\n            \/\/ Handle error or default navigation\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Deep Linking and App Store Optimization<\/h2>\n\n\n\n<p>Deep links directly impact your App Store Optimization (ASO) and organic search rankings. Both Apple and Google index deep link content, making your app discoverable through search. For a focused guide, see <a href=\"https:\/\/tolinku.com\/blog\/app-indexing-seo-mobile-apps\/\">app indexing and SEO for mobile apps<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">iOS App Indexing<\/h3>\n\n\n\n<p>Apple indexes Universal Links content in several ways:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Spotlight Search<\/strong>: Users can find your app content directly from iOS search<\/li>\n<li><strong>Siri Suggestions<\/strong>: iOS suggests your app based on user behavior patterns<\/li>\n<li><strong>Safari Search<\/strong>: Web search results can include your app content<\/li>\n<\/ol>\n\n\n\n<p>To optimize for iOS indexing:<\/p>\n\n\n\n<pre><code class=\"language-swift\">import CoreSpotlight\nimport MobileCoreServices\n\nfunc indexContent(product: Product) {\n    let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String)\n    attributeSet.title = product.name\n    attributeSet.contentDescription = product.description\n    attributeSet.thumbnailData = product.image.pngData()\n    \n    let item = CSSearchableItem(\n        uniqueIdentifier: &quot;product-\\(product.id)&quot;,\n        domainIdentifier: &quot;com.myapp.products&quot;,\n        attributeSet: attributeSet\n    )\n    \n    item.expirationDate = Date.distantFuture\n    \n    CSSearchableIndex.default().indexSearchableItems([item]) { error in\n        if let error = error {\n            print(&quot;Indexing error: \\(error.localizedDescription)&quot;)\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Android App Indexing<\/h3>\n\n\n\n<p>Google indexes App Links content for Google Search results. When users search for content that exists in your app, Google can show app installation buttons or direct app results.<\/p>\n\n\n\n<p>Enable app indexing in Android:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">import com.google.firebase.appindexing.Action\nimport com.google.firebase.appindexing.FirebaseAppIndex\nimport com.google.firebase.appindexing.builders.Actions\n\nfun indexContent(product: Product) {\n    val url = &quot;https:\/\/myapp.com\/product\/${product.id}&quot;\n    \n    val action = Actions.newView(\n        product.name,\n        url\n    )\n    \n    FirebaseAppIndex.getInstance().update(\n        Indexable.Builder()\n            .setName(product.name)\n            .setDescription(product.description)\n            .setUrl(url)\n            .setImage(product.imageUrl)\n            .build()\n    )\n    \n    FirebaseAppIndex.getInstance().start(action)\n}\n\noverride fun onStop() {\n    super.onStop()\n    \n    \/\/ Log the end of the view action\n    FirebaseAppIndex.getInstance().end(action)\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Common Deep Linking Problems and Solutions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Problem 1: Universal Links Not Opening the App<\/h3>\n\n\n\n<p>This is the most common iOS deep linking issue. Universal Links fail silently when misconfigured. See <a href=\"https:\/\/tolinku.com\/blog\/debugging-aasa-file\/\">debugging AASA files<\/a> and <a href=\"https:\/\/tolinku.com\/blog\/testing-universal-links\/\">testing Universal Links<\/a> for detailed troubleshooting.<\/p>\n\n\n\n<p><strong>Common causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>AASA file not accessible at the correct URL<\/li>\n<li>Invalid JSON syntax in AASA file<\/li>\n<li>Wrong Team ID or Bundle ID<\/li>\n<li>Associated Domains not configured correctly<\/li>\n<li>User has disabled Universal Links for your app<\/li>\n<\/ul>\n\n\n\n<p><strong>Debugging steps:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Verify AASA file: <code>curl -i https:\/\/yourdomain.com\/.well-known\/apple-app-site-association<\/code><\/li>\n<li>Check Apple&#39;s CDN: <code>curl -i https:\/\/app-site-association.cdn-apple.com\/a\/v1\/yourdomain.com<\/code><\/li>\n<li>Use Apple&#39;s validator: <a href=\"https:\/\/search.developer.apple.com\/appsearch-validation-tool\" rel=\"nofollow noopener\" target=\"_blank\">https:\/\/search.developer.apple.com\/appsearch-validation-tool<\/a><\/li>\n<li>Check device logs in Console.app for <code>swcd<\/code> process errors<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Problem 2: App Links Verification Failing<\/h3>\n\n\n\n<p>Android App Links verification happens asynchronously after app installation. If verification fails, your links open in the disambiguation dialog.<\/p>\n\n\n\n<p><strong>Common causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>assetlinks.json not served over HTTPS<\/li>\n<li>Certificate fingerprint mismatch<\/li>\n<li>Wrong package name<\/li>\n<li>HTTP redirects on the domain<\/li>\n<\/ul>\n\n\n\n<p><strong>Debugging steps:<\/strong><\/p>\n\n\n\n<pre><code class=\"language-bash\"># Test with Google&#39;s validator\nhttps:\/\/digitalassetlinks.googleapis.com\/v1\/statements:list?source.web.site=https:\/\/yourdomain.com&relation=delegate_permission\/common.handle_all_urls\n\n# Check verification status on device\nadb shell pm get-app-links com.your.package\n\n# Force reverification\nadb shell pm verify-app-links --re-verify com.your.package\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Problem 3: Deferred Deep Links Not Working<\/h3>\n\n\n\n<p>When deferred deep links fail, users land on the home screen after installing your app, defeating the purpose.<\/p>\n\n\n\n<p><strong>Common causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fingerprint mismatch between click and app open<\/li>\n<li>Too much time between click and install<\/li>\n<li>Privacy settings blocking fingerprint collection<\/li>\n<li>VPN or proxy changing IP address<\/li>\n<\/ul>\n\n\n\n<p><strong>Solutions:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Implement multiple matching strategies (deterministic + probabilistic)<\/li>\n<li>Use shorter TTLs for click data (2-24 hours)<\/li>\n<li>Provide manual fallback (let users enter a code)<\/li>\n<li>Track match rates and investigate drops<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Problem 4: Deep Links Working in Development but Not Production<\/h3>\n\n\n\n<p><strong>Common causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Using localhost URLs in production<\/li>\n<li>Forgetting to update AASA\/assetlinks.json for production domain<\/li>\n<li>SSL certificate issues on production domain<\/li>\n<li>URL encoding problems with special characters<\/li>\n<\/ul>\n\n\n\n<p><strong>Best practices:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use the same domain for development and production (with subdomains)<\/li>\n<li>Automate AASA\/assetlinks.json deployment<\/li>\n<li>Test with production builds before release<\/li>\n<li>URL encode all dynamic path components<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Best Practices for Deep Link Routes<\/h2>\n\n\n\n<p>Well-designed deep link routes make your app more discoverable and easier to integrate with other apps and marketing campaigns.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Design Human-Readable URLs<\/h3>\n\n\n\n<p>Good:<\/p>\n\n\n\n<pre><code>https:\/\/app.com\/product\/nike-air-max-90\nhttps:\/\/app.com\/user\/johndoe\nhttps:\/\/app.com\/article\/deep-linking-guide\n<\/code><\/pre>\n\n\n\n<p>Bad:<\/p>\n\n\n\n<pre><code>https:\/\/app.com\/p?id=12345\nhttps:\/\/app.com\/content?type=usr&q=98765\nhttps:\/\/app.com\/x\/AbC123XyZ\n<\/code><\/pre>\n\n\n\n<p>Human-readable URLs are easier to share, remember, and debug. They also perform better in search results.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Version Your Routes<\/h3>\n\n\n\n<p>Include versioning in your routes to maintain backwards compatibility:<\/p>\n\n\n\n<pre><code>https:\/\/app.com\/v1\/product\/123\nhttps:\/\/app.com\/v2\/product\/nike-air-max-90\n<\/code><\/pre>\n\n\n\n<p>Or use different paths for different versions:<\/p>\n\n\n\n<pre><code>https:\/\/app.com\/products\/123        # Old numeric IDs\nhttps:\/\/app.com\/product\/nike-air-max  # New slugs\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. Handle Missing Content Gracefully<\/h3>\n\n\n\n<p>When a deep link points to content that doesn&#39;t exist (deleted product, private profile, expired offer), don&#39;t crash or show an error screen.<\/p>\n\n\n\n<pre><code class=\"language-swift\">func handleProductDeepLink(productId: String) {\n    ProductService.fetch(id: productId) { result in\n        switch result {\n        case .success(let product):\n            navigateToProduct(product)\n        case .failure(let error):\n            if error == .notFound {\n                \/\/ Show search with the product ID pre-filled\n                navigateToSearch(query: productId)\n            } else {\n                \/\/ Show home with a subtle error message\n                navigateToHome(message: &quot;Product not available&quot;)\n            }\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4. Support Multiple URL Formats<\/h3>\n\n\n\n<p>Users might type URLs manually or modify them. Support common variations:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ All of these should work\n&quot;https:\/\/app.com\/product\/123&quot;\n&quot;https:\/\/app.com\/products\/123&quot;\n&quot;https:\/\/app.com\/p\/123&quot;\n&quot;https:\/\/app.com\/item\/123&quot;\n\n\/\/ In your router\nwhen {\n    path.matches(&quot;\/products?\/\\\\d+&quot;.toRegex()) -&gt; handleProduct(id)\n    path.matches(&quot;\/p\/\\\\d+&quot;.toRegex()) -&gt; handleProduct(id)  \n    path.matches(&quot;\/item\/\\\\d+&quot;.toRegex()) -&gt; handleProduct(id)\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">5. Add Context Parameters<\/h3>\n\n\n\n<p>Include optional parameters that provide context without breaking the core functionality:<\/p>\n\n\n\n<pre><code>https:\/\/app.com\/product\/123?source=email&amp;campaign=black-friday\nhttps:\/\/app.com\/invite\/abc123?referrer=johndoe&reward=10\n<\/code><\/pre>\n\n\n\n<p>These parameters help with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Attribution tracking<\/li>\n<li>Personalization<\/li>\n<li>A\/B testing<\/li>\n<li>Analytics<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Security Considerations<\/h2>\n\n\n\n<p>Deep links can be attack vectors if not properly secured. Users might craft malicious links to exploit your app. For comprehensive coverage, see <a href=\"https:\/\/tolinku.com\/blog\/deep-linking-security\/\">deep linking security: preventing hijacking and abuse<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Validate All Input<\/h3>\n\n\n\n<p>Never trust deep link parameters. Validate and sanitize everything:<\/p>\n\n\n\n<pre><code class=\"language-swift\">func handleDeepLink(url: URL) {\n    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true),\n          let path = components.path,\n          let productId = extractProductId(from: path),\n          isValidProductId(productId) else {\n        \/\/ Reject malformed URLs\n        return\n    }\n    \n    \/\/ Validate query parameters\n    let source = components.queryItems?.first(where: { $0.name == &quot;source&quot; })?.value\n    if let source = source, !isValidSource(source) {\n        \/\/ Log potential attack\n        logSecurityEvent(&quot;Invalid source parameter: \\(source)&quot;)\n        return\n    }\n    \n    \/\/ Safe to proceed\n    navigateToProduct(id: productId, source: source)\n}\n\nfunc isValidProductId(_ id: String) -&gt; Bool {\n    \/\/ Only alphanumeric and hyphens, reasonable length\n    let regex = &quot;^[a-zA-Z0-9-]{1,50}$&quot;\n    return id.range(of: regex, options: .regularExpression) != nil\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. Prevent Open Redirects<\/h3>\n\n\n\n<p>Don&#39;t allow arbitrary URLs in redirect parameters:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ BAD - allows open redirect attacks\nval redirectUrl = deepLink.getQueryParameter(&quot;redirect&quot;)\nopenWebView(redirectUrl)  \/\/ Could redirect to malicious site\n\n\/\/ GOOD - validate against allowlist\nval redirectPath = deepLink.getQueryParameter(&quot;redirect&quot;)\nval allowedDomains = listOf(&quot;myapp.com&quot;, &quot;trusted-partner.com&quot;)\n\nval fullUrl = URL(redirectPath)\nif (allowedDomains.contains(fullUrl.host)) {\n    openWebView(fullUrl)\n} else {\n    \/\/ Ignore or log suspicious activity\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. Implement Rate Limiting<\/h3>\n\n\n\n<p>Prevent abuse by limiting how often the same device can trigger certain actions through deep links:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">const rateLimiter = new Map();\nconst RATE_LIMIT_WINDOW = 60000; \/\/ 1 minute\nconst MAX_REQUESTS = 10;\n\nfunction checkRateLimit(deviceId, action) {\n    const key = `${deviceId}:${action}`;\n    const now = Date.now();\n    const requests = rateLimiter.get(key) || [];\n    \n    \/\/ Remove old requests\n    const recentRequests = requests.filter(time =&gt; now - time &lt; RATE_LIMIT_WINDOW);\n    \n    if (recentRequests.length &gt;= MAX_REQUESTS) {\n        return false; \/\/ Rate limit exceeded\n    }\n    \n    recentRequests.push(now);\n    rateLimiter.set(key, recentRequests);\n    return true;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4. Use HTTPS for Universal Links and App Links<\/h3>\n\n\n\n<p>Always use HTTPS for your deep links. HTTP links can be intercepted and modified:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Universal Links require HTTPS<\/li>\n<li>App Links require HTTPS for verification<\/li>\n<li>Custom schemes can&#39;t be encrypted, another reason to avoid them<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Deep Links<\/h2>\n\n\n\n<p>Comprehensive deep link testing prevents broken user experiences. Test across devices, OS versions, and edge cases. See <a href=\"https:\/\/tolinku.com\/blog\/deep-link-testing-tools\/\">deep link testing tools<\/a> for available testing utilities.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Manual Testing Checklist<\/h3>\n\n\n\n<p><strong>iOS Testing:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><input disabled=\"\" type=\"checkbox\"> Links open app when installed<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links fall back to web when app not installed<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Deferred deep links work after fresh install<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links work from Safari, Mail, Messages, and third-party apps<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Long-press link shows app preview (iOS 13+)<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links work after app updates<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links work when app is killed vs backgrounded<\/li>\n<\/ul>\n\n\n\n<p><strong>Android Testing:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><input disabled=\"\" type=\"checkbox\"> Links skip disambiguation dialog (App Links verified)<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links work from Chrome, Gmail, SMS<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Deferred deep links work after install from Play Store<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links work with different default browsers<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links work on Android 6+ (App Links) and older versions<\/li>\n<li><input disabled=\"\" type=\"checkbox\"> Links handle both http and https schemes<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Automated Testing<\/h3>\n\n\n\n<p>Write unit tests for your deep link routing logic:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ Swift XCTest example\nfunc testProductDeepLink() {\n    let router = DeepLinkRouter()\n    let url = URL(string: &quot;https:\/\/myapp.com\/product\/123?source=test&quot;)!\n    \n    let result = router.parse(url: url)\n    \n    XCTAssertEqual(result.destination, .product(id: &quot;123&quot;))\n    XCTAssertEqual(result.parameters[&quot;source&quot;], &quot;test&quot;)\n}\n\nfunc testInvalidDeepLink() {\n    let router = DeepLinkRouter()\n    let url = URL(string: &quot;https:\/\/myapp.com\/invalid\/path&quot;)!\n    \n    let result = router.parse(url: url)\n    \n    XCTAssertEqual(result.destination, .home)\n}\n<\/code><\/pre>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ Kotlin JUnit example\n@Test\nfun testProductDeepLink() {\n    val router = DeepLinkRouter()\n    val uri = Uri.parse(&quot;https:\/\/myapp.com\/product\/123?source=test&quot;)\n    \n    val result = router.parse(uri)\n    \n    assertEquals(Destination.Product(id = &quot;123&quot;), result.destination)\n    assertEquals(&quot;test&quot;, result.parameters[&quot;source&quot;])\n}\n\n@Test\nfun testDeferredDeepLink() = runBlockingTest {\n    val service = DeepLinkService()\n    \n    \/\/ Simulate click before install\n    service.saveClickData(&quot;device123&quot;, &quot;https:\/\/myapp.com\/product\/456&quot;)\n    \n    \/\/ Simulate app open after install\n    val destination = service.retrieveDestination(&quot;device123&quot;)\n    \n    assertEquals(&quot;https:\/\/myapp.com\/product\/456&quot;, destination)\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">End-to-End Testing<\/h3>\n\n\n\n<p>Use tools to simulate the full deep link flow:<\/p>\n\n\n\n<p><strong>iOS Simulator:<\/strong><\/p>\n\n\n\n<pre><code class=\"language-bash\"># Open deep link in simulator\nxcrun simctl openurl booted &quot;https:\/\/myapp.com\/product\/123&quot;\n\n# Test custom scheme\nxcrun simctl openurl booted &quot;myapp:\/\/product\/123&quot;\n<\/code><\/pre>\n\n\n\n<p><strong>Android Emulator:<\/strong><\/p>\n\n\n\n<pre><code class=\"language-bash\"># Test deep link\nadb shell am start -W -a android.intent.action.VIEW -d &quot;https:\/\/myapp.com\/product\/123&quot;\n\n# Test with specific package\nadb shell am start -W -a android.intent.action.VIEW -d &quot;https:\/\/myapp.com\/product\/123&quot; com.myapp.package\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Analytics and Attribution<\/h2>\n\n\n\n<p>Measuring deep link performance helps you understand which channels drive the most valuable users. For a focused guide, see <a href=\"https:\/\/tolinku.com\/blog\/deep-link-analytics-measuring-what-matters\/\">deep link analytics: measuring what matters<\/a>. Track these key metrics:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Essential Deep Link Metrics<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Click-through Rate (CTR)<\/strong>: Clicks on your deep links divided by impressions<\/li>\n<li><strong>Install Attribution<\/strong>: Which deep links lead to app installs<\/li>\n<li><strong>Deferred Deep Link Success Rate<\/strong>: Percentage of installs that successfully route to intended destination<\/li>\n<li><strong>Time to Action<\/strong>: How long users take to complete desired action after clicking<\/li>\n<li><strong>Retention by Source<\/strong>: User retention segmented by deep link source<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Implementing Attribution<\/h3>\n\n\n\n<p>Add attribution parameters to all your deep links:<\/p>\n\n\n\n<pre><code>https:\/\/myapp.com\/product\/123?\n  utm_source=email&amp;\n  utm_medium=newsletter&amp;\n  utm_campaign=black_friday&amp;\n  utm_content=hero_image\n<\/code><\/pre>\n\n\n\n<p>Track these events in your analytics:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ Track deep link opened\nAnalytics.track(&quot;Deep Link Opened&quot;, properties: [\n    &quot;url&quot;: url.absoluteString,\n    &quot;source&quot;: parameters[&quot;utm_source&quot;] ?? &quot;direct&quot;,\n    &quot;medium&quot;: parameters[&quot;utm_medium&quot;] ?? &quot;none&quot;,\n    &quot;campaign&quot;: parameters[&quot;utm_campaign&quot;] ?? &quot;none&quot;,\n    &quot;content&quot;: parameters[&quot;utm_content&quot;] ?? &quot;none&quot;,\n    &quot;destination&quot;: destination.analyticsName\n])\n\n\/\/ Track conversion\nAnalytics.track(&quot;Deep Link Conversion&quot;, properties: [\n    &quot;action&quot;: &quot;purchase&quot;,\n    &quot;value&quot;: purchase.total,\n    &quot;source&quot;: session.attributionSource,\n    &quot;time_to_convert&quot;: Date().timeIntervalSince(session.startTime)\n])\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Building Attribution Funnels<\/h3>\n\n\n\n<p>Track users through the entire journey:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Link Click<\/strong>: User clicks deep link<\/li>\n<li><strong>App Open<\/strong>: App launches (immediate or deferred)<\/li>\n<li><strong>Screen View<\/strong>: User arrives at intended destination  <\/li>\n<li><strong>Engagement<\/strong>: User interacts with content<\/li>\n<li><strong>Conversion<\/strong>: User completes desired action<\/li>\n<\/ol>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Server-side funnel tracking\nconst funnel = {\n  linkClick: async (clickId, url, metadata) =&gt; {\n    await db.funnelEvents.create({\n      clickId,\n      event: &#39;link_click&#39;,\n      url,\n      timestamp: new Date(),\n      ...metadata\n    });\n  },\n  \n  appOpen: async (clickId, deferred = false) =&gt; {\n    await db.funnelEvents.create({\n      clickId,\n      event: &#39;app_open&#39;,\n      deferred,\n      timestamp: new Date()\n    });\n  },\n  \n  conversion: async (clickId, action, value) =&gt; {\n    await db.funnelEvents.create({\n      clickId,\n      event: &#39;conversion&#39;,\n      action,\n      value,\n      timestamp: new Date()\n    });\n    \n    \/\/ Calculate attribution\n    const events = await db.funnelEvents.find({ clickId });\n    const timeToConvert = events[events.length-1].timestamp - events[0].timestamp;\n    \n    await db.attributions.create({\n      clickId,\n      source: events[0].utm_source,\n      timeToConvert,\n      value\n    });\n  }\n};\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Use Cases and Examples<\/h2>\n\n\n\n<p>Deep linking enables countless user experiences. Here are real-world examples across different industries:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">E-commerce<\/h3>\n\n\n\n<p><strong>Product Sharing<\/strong>: Users share products with friends who can tap to view in the app<\/p>\n\n\n\n<pre><code>https:\/\/shop.app\/product\/summer-dress-floral?size=M&amp;color=blue\n<\/code><\/pre>\n\n\n\n<p><strong>Abandoned Cart Recovery<\/strong>: Email reminders with deep links straight to checkout<\/p>\n\n\n\n<pre><code>https:\/\/shop.app\/cart?recover=abc123&amp;discount=10\n<\/code><\/pre>\n\n\n\n<p><strong>Promotional Campaigns<\/strong>: QR codes in stores that open limited-time offers<\/p>\n\n\n\n<pre><code>https:\/\/shop.app\/promo\/SUMMER2024?store=NYC001\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Social Media<\/h3>\n\n\n\n<p><strong>Content Sharing<\/strong>: Links that open specific posts, profiles, or conversations<\/p>\n\n\n\n<pre><code>https:\/\/social.app\/post\/xyz789\nhttps:\/\/social.app\/user\/alice\/followers\nhttps:\/\/social.app\/messages\/thread\/123\n<\/code><\/pre>\n\n\n\n<p><strong>User Invitations<\/strong>: Referral links that track who invited whom<\/p>\n\n\n\n<pre><code>https:\/\/social.app\/invite?code=ALICE123&amp;reward=premium_month\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Media and Entertainment<\/h3>\n\n\n\n<p><strong>Content Deep Links<\/strong>: Direct links to videos, articles, or podcasts<\/p>\n\n\n\n<pre><code>https:\/\/media.app\/watch\/show\/season\/1\/episode\/5?t=180\nhttps:\/\/media.app\/article\/breaking-news-story\nhttps:\/\/media.app\/podcast\/episode\/123?start=1250\n<\/code><\/pre>\n\n\n\n<p><strong>Playlist Sharing<\/strong>: Share curated content collections<\/p>\n\n\n\n<pre><code>https:\/\/media.app\/playlist\/workout-mix-2024?creator=johndoe\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Financial Services<\/h3>\n\n\n\n<p><strong>Payment Links<\/strong>: Request or send money with pre-filled amounts<\/p>\n\n\n\n<pre><code>https:\/\/pay.app\/send?to=alice@example.com&amp;amount=50&amp;note=dinner\n<\/code><\/pre>\n\n\n\n<p><strong>Document Review<\/strong>: Deep link to specific documents requiring signature<\/p>\n\n\n\n<pre><code>https:\/\/bank.app\/documents\/loan-application-2024?action=sign\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Food Delivery<\/h3>\n\n\n\n<p><strong>Restaurant Menus<\/strong>: Link directly to a restaurant&#39;s menu<\/p>\n\n\n\n<pre><code>https:\/\/food.app\/restaurant\/pizza-palace\/menu?category=pizzas\n<\/code><\/pre>\n\n\n\n<p><strong>Reorder Previous Orders<\/strong>: One-tap reordering<\/p>\n\n\n\n<pre><code>https:\/\/food.app\/order\/repeat?id=12345&amp;address=home\n<\/code><\/pre>\n\n\n\n<p><strong>Group Orders<\/strong>: Share a group order for others to add items<\/p>\n\n\n\n<pre><code>https:\/\/food.app\/group-order\/abc123\/join\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Migration Strategies<\/h2>\n\n\n\n<p>If you&#39;re moving from another deep linking solution or building deep linking for an existing app, plan your migration carefully.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Migrating from Firebase Dynamic Links<\/h3>\n\n\n\n<p>With Firebase Dynamic Links shutting down, many apps need migration strategies. For a detailed walkthrough, see <a href=\"https:\/\/tolinku.com\/blog\/firebase-dynamic-links-shutdown\/\">Firebase Dynamic Links shutdown: what to do next<\/a> and the <a href=\"https:\/\/tolinku.com\/blog\/migrate-from-firebase-dynamic-links\/\">migration guide<\/a>.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Audit Existing Links<\/strong>: Export all your Dynamic Links and their destinations<\/li>\n<li><strong>Set Up Redirects<\/strong>: Configure your new deep linking service to handle old URLs<\/li>\n<li><strong>Update Marketing Materials<\/strong>: Replace Dynamic Links in emails, ads, and print<\/li>\n<li><strong>Monitor Analytics<\/strong>: Track both old and new links during transition<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Migrating from Branch<\/h3>\n\n\n\n<p>When migrating from Branch or similar platforms:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Redirect old Branch links to new deep links\napp.get(&#39;\/:branch_key\/*&#39;, async (req, res) =&gt; {\n  const branchKey = req.params.branch_key;\n  const path = req.params[0];\n  \n  \/\/ Map old Branch links to new structure\n  const mapping = await db.linkMappings.findOne({ branchKey });\n  \n  if (mapping) {\n    res.redirect(301, `https:\/\/newdomain.com\/${mapping.newPath}`);\n  } else {\n    res.redirect(301, &#39;https:\/\/newdomain.com&#39;);\n  }\n});\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Supporting Multiple URL Schemes<\/h3>\n\n\n\n<p>During migration, support both old and new URL formats:<\/p>\n\n\n\n<pre><code class=\"language-swift\">func handleUniversalLink(url: URL) -&gt; Bool {\n    \/\/ Handle new domain\n    if url.host == &quot;newdomain.com&quot; {\n        return handleNewDeepLink(url)\n    }\n    \n    \/\/ Handle legacy domains\n    if url.host == &quot;olddomain.com&quot; || url.host == &quot;branch.io&quot; {\n        if let mappedURL = mapLegacyURL(url) {\n            return handleNewDeepLink(mappedURL)\n        }\n    }\n    \n    return false\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Performance Optimization<\/h2>\n\n\n\n<p>Deep link routing should be fast. Users expect instant navigation after tapping a link.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Optimize Route Matching<\/h3>\n\n\n\n<p>Use efficient data structures for route matching:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ Instead of sequential regex matching\nfun matchRoute(path: String): Route? {\n    for (route in routes) {\n        if (route.pattern.matches(path)) {\n            return route\n        }\n    }\n    return null\n}\n\n\/\/ Use a trie or route tree\nclass RouteTree {\n    private val root = RouteNode()\n    \n    fun addRoute(pattern: String, handler: RouteHandler) {\n        var node = root\n        val segments = pattern.split(&quot;\/&quot;).filter { it.isNotEmpty() }\n        \n        for (segment in segments) {\n            node = node.children.getOrPut(segment) { RouteNode() }\n        }\n        \n        node.handler = handler\n    }\n    \n    fun match(path: String): RouteMatch? {\n        var node = root\n        val segments = path.split(&quot;\/&quot;).filter { it.isNotEmpty() }\n        val params = mutableMapOf&lt;String, String&gt;()\n        \n        for ((index, segment) in segments.withIndex()) {\n            node = node.children[segment] \n                ?: node.children[&quot;:param&quot;]  \/\/ Parameter match\n                ?: return null\n                \n            if (node.children.containsKey(&quot;:param&quot;)) {\n                \/\/ Extract parameter value\n                params[node.paramName] = segment\n            }\n        }\n        \n        return node.handler?.let { RouteMatch(it, params) }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Preload Critical Data<\/h3>\n\n\n\n<p>Start loading data as soon as you receive the deep link:<\/p>\n\n\n\n<pre><code class=\"language-swift\">class DeepLinkHandler {\n    func handleDeepLink(_ url: URL) {\n        \/\/ Parse URL immediately\n        guard let route = parseRoute(url) else { return }\n        \n        \/\/ Start loading data before animation completes\n        switch route {\n        case .product(let id):\n            ProductService.shared.preload(productId: id)\n            navigateToProduct(id: id)\n            \n        case .user(let username):\n            UserService.shared.preload(username: username)\n            navigateToUser(username: username)\n        }\n    }\n}\n\n\/\/ In the destination view controller\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    \/\/ Data might already be loaded\n    if let product = ProductService.shared.getCached(productId: productId) {\n        display(product)\n    } else {\n        \/\/ Load if not cached\n        loadProduct()\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Handle Network Failures Gracefully<\/h3>\n\n\n\n<p>Deep links often require network requests. Plan for failures:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">sealed class DeepLinkResult {\n    data class Success(val destination: Destination) : DeepLinkResult()\n    data class NetworkError(val error: Error, val fallback: Destination) : DeepLinkResult()\n    data class NotFound(val suggestion: Destination?) : DeepLinkResult()\n}\n\nfun resolveDeepLink(url: Uri): DeepLinkResult {\n    return try {\n        val destination = parseAndValidate(url)\n        DeepLinkResult.Success(destination)\n    } catch (e: NetworkException) {\n        \/\/ Provide offline fallback\n        val fallback = getFallbackDestination(url)\n        DeepLinkResult.NetworkError(e, fallback)\n    } catch (e: NotFoundException) {\n        \/\/ Suggest alternative\n        val suggestion = findSimilarContent(url)\n        DeepLinkResult.NotFound(suggestion)\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Future of Deep Linking<\/h2>\n\n\n\n<p>The deep linking landscape continues to evolve with new platform features and user expectations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">App Clips and Instant Apps<\/h3>\n\n\n\n<p>Apple&#39;s App Clips and Google&#39;s Instant Apps blur the line between web and native:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ App Clip deep link handling\nimport AppClip\n\nguard let scene = window?.windowScene else { return }\n\nif let userActivity = scene.userActivity,\n   let incomingURL = userActivity.webpageURL {\n    handleAppClipDeepLink(incomingURL)\n}\n\n\/\/ Store data for full app\nlet sharedDefaults = UserDefaults(suiteName: &quot;group.com.app.shared&quot;)\nsharedDefaults?.set(cartItems, forKey: &quot;clipCart&quot;)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Privacy-Preserving Attribution<\/h3>\n\n\n\n<p>With increasing privacy regulations and platform restrictions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>iOS 14.5+ requires ATT consent for IDFA<\/li>\n<li>Android 12+ restricts device identifiers<\/li>\n<li>Browsers block third-party cookies<\/li>\n<\/ul>\n\n\n\n<p>Modern deep linking platforms use privacy-preserving techniques:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>On-device matching when possible<\/li>\n<li>Aggregated attribution reporting  <\/li>\n<li>Consent-based tracking<\/li>\n<li>First-party data strategies<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Web-to-App Journeys<\/h3>\n\n\n\n<p>Progressive Web Apps (PWAs) and improved web capabilities create new deep linking scenarios:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Check if native app is installed\nif (&#39;getInstalledRelatedApps&#39; in navigator) {\n  const relatedApps = await navigator.getInstalledRelatedApps();\n  const nativeApp = relatedApps.find(app =&gt; app.platform === &#39;play&#39;);\n  \n  if (nativeApp) {\n    \/\/ Deep link to native app\n    window.location = &#39;myapp:\/\/path\/to\/content&#39;;\n  } else {\n    \/\/ Show install prompt or continue in PWA\n    showAppInstallBanner();\n  }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Deep linking is foundational infrastructure for mobile apps. Implementing it correctly means better engagement, higher conversion rates, and app content that shows up in search results.<\/p>\n\n\n\n<p>Start with the basics: Universal Links on iOS and App Links on Android. Add deferred deep linking to capture users across the install boundary. Track attribution to understand what actually drives growth. Then expand into referral programs, personalized onboarding, and cross-platform journeys.<\/p>\n\n\n\n<p>The technical details matter, but the user experience matters more. Every deep link should deliver on its promise: take users directly to what they came for, without friction.<\/p>\n\n\n\n<p>Whether you build deep linking yourself or use a platform like <a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku<\/a>, the investment pays off. In a mobile-first world, deep links are not optional. They are essential infrastructure for any serious app.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Everything you need to know about deep linking for mobile apps. Learn types, implementation, and best practices for iOS and Android.<\/p>\n","protected":false},"author":2,"featured_media":360,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"The Complete Guide to Deep Linking in 2026","rank_math_description":"Everything you need to know about deep linking for mobile apps. Learn types, implementation, and best practices for iOS and Android deep links.","rank_math_focus_keyword":"deep linking guide","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-complete-guide-deep-linking-2026.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-complete-guide-deep-linking-2026.png","footnotes":""},"categories":[11],"tags":[25,23,28,20,21,24,29,30,22,26],"class_list":["post-361","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deep-linking","tag-android","tag-app-links","tag-attribution","tag-deep-linking","tag-deferred-deep-linking","tag-ios","tag-mobile-marketing","tag-sdks","tag-universal-links","tag-user-acquisition"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/361","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=361"}],"version-history":[{"count":4,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/361\/revisions"}],"predecessor-version":[{"id":2760,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/361\/revisions\/2760"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/360"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=361"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=361"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=361"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}