Table of Contents

    Book an Appointment

    How Did We Discover the Challenge with Web-to-App Install Attribution?

    While working on a mobile commerce platform for a global retail client, we encountered a classic marketing-to-engineering disconnect. The marketing team launched a high-profile web campaign displaying a banner: “Install the app and get a 20% discount coupon.” The user flow was intended to be seamless: a user clicks the web popup, gets redirected to the App Store or Google Play, installs the app, logs in, and automatically receives the reward.

    During early staging deployments, we realized the attribution chain was breaking completely. If users already had the app installed, standard Universal Links and App Links routed them perfectly. However, for a fresh install, the contextual payload containing the campaign ID was dropped during the app store routing. By the time the user opened the app for the first time, our system had no idea they originated from the web popup campaign.

    This missing context meant coupons were not being applied, resulting in frustrated users and a distorted view of the campaign’s return on investment. This challenge inspired this technical breakdown so other engineering teams can avoid the pitfalls of deferred deep linking and correctly implement cross-platform install attribution.

    Why Is Tracking the App Install Source After a Fresh Install So Complex?

    In a standard web environment, passing state via URL parameters or cookies is trivial. In the mobile ecosystem, the App Store and Google Play act as opaque walls. When a user is redirected to an app store, the browser session is detached from the eventual app download and launch.

    To deliver the coupon upon login, our architecture needed to maintain state across this divide. We needed a deferred deep link—a mechanism that remembers the user’s intended destination or campaign origin before the app is downloaded, and then passes that data into the app upon its very first launch. Only by securely capturing this campaign ID on the mobile client (in Kotlin and Swift) could we pass it to our backend microservices during the user login event to trigger the coupon allocation.

    Where Did Our Initial App Attribution Architecture Fail?

    Our initial investigation revealed several architectural oversights. The logs showed that our standard deep link handling in the Android MainActivity and the iOS SceneDelegate were executing correctly, but the incoming intent payloads were entirely empty on first launch.

    We had initially assumed we could scrape referrer data out of the box. However, iOS provides absolutely no native install referrer mechanism comparable to Android due to strict privacy paradigms. Furthermore, the web platform was dynamically generating campaign IDs based on user session data, which meant we couldn’t rely on static, pre-generated promo codes. The bottleneck wasn’t just catching a link; it was reliably bridging the identity gap between a web click and an anonymous app launch without violating platform privacy policies.

    What Were the Options for Deferred Deep Linking Solutions?

    We evaluated several strategies to pass campaign parameters safely through the install process. The primary requirement was stability across both Android and iOS, ensuring we could connect the web campaign to the app install and the subsequent user login.

    Could We Rely Solely on the Google Play Install Referrer API?

    For Android, the Google Play Install Referrer API is the official way to capture install sources. We considered writing a custom integration using Kotlin to extract the `referrer` string and parse our campaign parameters. However, this approach completely ignored iOS. Building a highly reliable solution for Android while leaving iOS entirely unhandled was not viable for a cross-platform product.

    Should We Continue Using Firebase Dynamic Links?

    Firebase Dynamic Links (FDL) was historically the go-to solution for this exact problem. It provided a unified API for deferred deep linking. However, FDL is officially deprecated and slated for shutdown. Building a new core architecture on a deprecated service is an unacceptable risk for enterprise applications.

    Could We Build a Custom Device Fingerprinting Service?

    We considered building an in-house attribution engine that would record IP address, user agent, and timestamp upon the web click, and then attempt to probabilistically match those details when the app launched. We quickly discarded this. Not only is device fingerprinting highly inaccurate on modern cellular networks, but it also aggressively violates Apple’s App Tracking Transparency (ATT) guidelines. We refuse to compromise our clients’ compliance posture.

    Was Integrating an Enterprise Mobile Measurement Partner (MMP) the Best Choice?

    We ultimately decided to integrate a dedicated Mobile Measurement Partner (MMP) like Branch.io or AppsFlyer. These public, industry-standard platforms specialize in managing the complex heuristics of deferred deep linking. They leverage native mechanisms like SKAdNetwork on iOS and the Install Referrer API on Android under a single unified SDK. This decoupled the complexity of app store routing from our application logic, allowing us to focus purely on the business requirement: extracting the campaign ID and rewarding the user.

    How Did We Implement Deferred Deep Link Tracking in Kotlin and Swift?

    Our final implementation involved configuring the chosen MMP SDK to intercept the initial app launch, extract the deferred parameters, and hold them in memory until the user successfully logged in. Organizations that hire app developer to create a mobile app often overlook the crucial step of decoupling the deep link handling from immediate UI navigation, especially when authentication is required.

    Here is a sanitized representation of our approach for both platforms.

    Android Implementation (Kotlin)

    On Android, we initialized the SDK in our custom Application class and captured the deferred parameters in the launcher activity. We ensured the parameters were saved securely so they could be appended to the login request later.

    // Application Initialization
    class ECommerceApplication : Application() {
        override fun onCreate() {
            super.onCreate()
            // Initialize the MMP SDK
            AttributionManager.initialize(this, "YOUR_SDK_KEY")
        }
    }
    // Launcher Activity
    class SplashActivity : AppCompatActivity() {
        override fun onStart() {
            super.onStart()
            
            AttributionManager.initSession(intent) { params, error ->
                if (error == null && params != null) {
                    // Check if this is a fresh install via deferred link
                    val isFirstSession = params.optBoolean("+is_first_session", false)
                    val campaignId = params.optString("campaign_id", null)
                    
                    if (isFirstSession && campaignId != null) {
                        // Store campaign locally for the login flow
                        SecurePreferences.saveCampaignContext(this, campaignId)
                    }
                }
                navigateToNextScreen()
            }
        }
        
        override fun onNewIntent(intent: Intent) {
            super.onNewIntent(intent)
            setIntent(intent)
            AttributionManager.reInitSession(intent)
        }
    }
    

    iOS Implementation (Swift)

    On iOS, we handled the universal link and deferred deep link callbacks within the AppDelegate. Since iOS requires explicit handling of User Activities, we mapped the MMP callbacks accordingly.

    import UIKit
    @main
    class AppDelegate: UIResponder, UIApplicationDelegate {
        func application(_ application: UIApplication, 
                         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            
            // Initialize MMP SDK
            AttributionManager.shared().initSession(launchOptions: launchOptions) { (params, error) in
                guard error == nil, let params = params as? [String: AnyObject] else { return }
                
                let isFirstSession = params["+is_first_session"] as? Bool ?? false
                if let campaignId = params["campaign_id"] as? String, isFirstSession {
                    // Store securely for login payload
                    SecureStorage.shared.save(key: "pending_campaign_id", value: campaignId)
                }
            }
            return true
        }
        // Handle standard Universal Links
        func application(_ application: UIApplication, 
                         continue userActivity: NSUserActivity, 
                         restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
            
            return AttributionManager.shared().continue(userActivity)
        }
    }
    

    Once the user completed the authentication flow, our login repository retrieved the stored campaign_id and included it in the backend API payload. The backend validated the user, registered the campaign success, and seamlessly deposited the 20% discount coupon into the user’s new account.

    What Are the Core Lessons for Engineering Teams Implementing Attribution?

    Through resolving this attribution gap, our architecture team reinforced several crucial engineering practices. When you hire software developer teams for enterprise projects, you expect them to anticipate these systemic challenges.

    • Decouple Deep Links from Navigation: A deferred deep link rarely means you should navigate the user immediately to a specific screen upon first launch. Often, you must cache the link data, push the user through an onboarding or login flow, and act on the data later.
    • Do Not Build In-House Fingerprinting: Attempting to build your own probabilistic matching engine is an anti-pattern. It is expensive to maintain, highly inaccurate, and likely to trigger app store rejections due to privacy violations. Use established MMPs.
    • Future-Proof Your Tooling: Relying on deprecated tools like Firebase Dynamic Links causes severe tech debt. Always migrate to actively supported platforms for core attribution architecture.
    • Backend Validation is Mandatory: Never trust the client application to award a coupon directly. The mobile app should only provide the context (the campaign_id) to the backend. The backend must independently verify the campaign validity and user eligibility.
    • Handle the iOS ATT Prompt Gracefully: Ensure your attribution logic respects Apple’s App Tracking Transparency framework. Deterministic tracking often requires user consent, meaning your application must be prepared to handle fallback probabilistic data.

    How Can Your Architecture Benefit From Proper Deep Linking?

    Connecting a web marketing campaign to a specific user action after a fresh mobile app installation requires careful state management across the app store divide. By abandoning deprecated tools and custom fingerprinting in favor of an industry-standard MMP integration, we established a resilient, cross-platform attribution pipeline. This solution ensured our client could confidently invest in web-to-app conversion strategies without losing visibility into their campaign performance.

    If your enterprise is struggling with mobile attribution, deep linking, or complex cross-platform architecture, contact us. Our structured delivery practices and experienced engineering teams can help you build scalable, measurable, and reliable digital products.

    Social Hashtags

    #DeferredDeepLinking #DeepLinking #MobileAppDevelopment #iOSDevelopment #AndroidDevelopment #Kotlin #Swift #MobileAttribution #AppDevelopment #SoftwareDevelopment #MobileMarketing #AppAnalytics #MMP #CrossPlatformDevelopment #EnterpriseSoftware

     

    Frequently Asked Questions