Deep Linking in Mobile App

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Deep Linking in Mobile App
Medium
from 1 business day to 3 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Implementation of Deep Linking in Mobile Application

Deep Link is a URL that opens specific screen in mobile app. Not homepage, but exactly needed content: product, profile, article, payment page. Seems simple until you face difference between Custom URL Scheme, Universal Links, App Links, Deferred Deep Links and how each breaks in its specific place.

Three types of deep links

Custom URL Scheme (myapp://product/123) — simplest but most unreliable. If app not installed — browser shows error, no fallback. Multiple apps can register one scheme — unpredictable which opens. Suitable only for internal scenarios: cross-app communication within own ecosystem.

Universal Links (iOS) / App Links (Android) — HTTP/HTTPS links that operating system intercepts and opens in app instead of browser. If app not installed — normal browser. Reliable, verified, correct fallback. This is production standard.

Deferred Deep Links — link saved even if app not installed. User taps link → App Store → installs app → opens → lands on right screen. Implemented through Firebase Dynamic Links (deprecated since 2025-08), Branch.io or Adjust.

Universal Links on iOS

Requires apple-app-site-association file on server:

// https://yourdomain.com/.well-known/apple-app-site-association
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appIDs": ["TEAMID.com.company.app"],
        "components": [
          { "/": "/product/*", "comment": "Product pages" },
          { "/": "/profile/*" },
          { "/": "/order/*" },
          { "/": "/promo/*", "?": { "ref": "?" } }
        ]
      }
    ]
  }
}

AASA must be returned with Content-Type: application/json, without redirects, with code 200. Apple CDN caches it aggressively — changes take effect with delay up to 48 hours (Apple periodically visits AASA in background). On iOS 16+ added "mode": "developer" for faster AASA update in debug.

In Info.plist — Associated Domains:

<key>com.apple.developer.associated-domains</key>
<array>
    <string>applinks:yourdomain.com</string>
    <string>applinks:www.yourdomain.com</string>
</array>

Handling in SceneDelegate:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let url = userActivity.webpageURL else { return }
    DeepLinkRouter.shared.handle(url: url)
}

App Links on Android

Analog of Universal Links. File assetlinks.json on server:

// https://yourdomain.com/.well-known/assetlinks.json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.company.app",
    "sha256_cert_fingerprints": ["AA:BB:CC:..."]
  }
}]

SHA256 fingerprint taken from keystore: keytool -list -v -keystore release.jks. Debug and release — different fingerprints, both needed in production assetlinks.json or use different domains.

In AndroidManifest.xml:

<activity android:name=".MainActivity">
    <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="yourdomain.com"
            android:pathPrefix="/product/" />
    </intent-filter>
</activity>

android:autoVerify="true" launches domain verification on install. Check verification: adb shell pm get-app-links com.company.app. If STATE_APPROVED — App Links work. If STATE_NO_RESPONSE or STATE_FAILED_VERIFICATION — problem with AASA or certificate.

Common problem on Android 12+: even with successful verification user can choose "Open in browser" in system settings. App cannot control this programmatically.

Router on client

Centralized router — mandatory. No if (url.contains("product")) scattered through code.

// Android
class DeepLinkRouter {
    fun handle(intent: Intent, navController: NavController) {
        val uri = intent.data ?: return
        val path = uri.path ?: return

        val route = when {
            path.matches(Regex("/product/(\\d+)")) -> {
                val productId = uri.lastPathSegment ?: return
                Route.ProductDetail(productId)
            }
            path.matches(Regex("/order/(\\w+)")) -> {
                Route.OrderDetail(uri.lastPathSegment ?: return)
            }
            path == "/profile" -> Route.Profile
            path.startsWith("/promo/") -> {
                val ref = uri.getQueryParameter("ref")
                Route.Promo(uri.lastPathSegment ?: return, ref)
            }
            else -> {
                // Unknown route — open in browser
                openInBrowser(uri)
                return
            }
        }
        navController.navigate(route)
    }
}
// iOS
final class DeepLinkRouter {
    func handle(url: URL) {
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
        let pathComponents = components.path.split(separator: "/").map(String.init)

        switch pathComponents.first {
        case "product" where pathComponents.count == 2:
            navigationManager.push(.productDetail(id: pathComponents[1]))
        case "order" where pathComponents.count == 2:
            navigationManager.push(.orderDetail(id: pathComponents[1]))
        case "profile":
            navigationManager.push(.profile)
        default:
            UIApplication.shared.open(url) // fallback to browser
        }
    }
}

Deferred Deep Links through Branch.io

Firebase Dynamic Links officially deprecated since August 2025. Alternatives: Branch.io, Adjust, AppsFlyer, Airbridge — all provide SDK for iOS and Android.

Branch.io SDK tracks click before install and restores parameters after first launch:

Branch.getInstance().initSession(
    branchReferralInitListener = { params, error ->
        if (error == null && params != null) {
            val productId = params.getString("product_id")
            val ref = params.getString("ref")
            if (productId != null) {
                deepLinkRouter.navigate(Route.ProductDetail(productId))
            }
        }
    },
    isReferrable = true,
    activity = this
)

Branch initSession called on each launch — SDK determines whether this is organic launch or link transition.

Testing

App Links verification: adb shell am start -W -a android.intent.action.VIEW -d "https://yourdomain.com/product/123" com.company.app

Universal Links verification on simulator — impossible. Only on physical device via xcrun simctl openurl booted "https://yourdomain.com/product/123" for iOS 14+ simulator, or via Notes.app → tap link.

Check AASA: curl -I https://yourdomain.com/.well-known/apple-app-site-association — needs 200, without redirects.

Apple provides validator: https://app-site-association.cdn-apple.com/a/v1/yourdomain.com — how Apple caches your AASA file.

Typical errors

Redirect on CDN breaks App Links. If yourdomain.com redirects to www.yourdomain.com, and AASA on one only — verification fails. AASA needed on both domains.

Wrong Content-Type. AASA with Content-Type: text/html — Apple doesn't accept. Only application/json.

Deep link on cold start vs warm start. On Android Intent comes in onCreate on cold start and in onNewIntent on warm start. If handling only in onCreate — deep link on warm start ignored. Handle in both places.

Navigation before initialization. Router tries navigate before NavController ready. Need queue of pending deep links, processed after navigation initialization.

Implementation of Universal Links + App Links + router + deferred deep links: 2-4 weeks. Cost calculated individually.