Een gebruiker tikt op je link — een gedeeld product, een uitnodiging, een wachtwoordherstel — en de app is nog niet geïnstalleerd. Hij belandt in de App Store of Google Play, installeert de app, opent hem, en… wordt op een generiek beginscherm gedumpt. De context van waarop hij tikte is weg. Precies dat kapotte moment moet oplossen, en jarenlang deden de meeste teams dat met Firebase Dynamic Links. Die optie bestaat niet meer.

Dit artikel legt uit hoe we een systeem voor uitgestelde deep linking bouwden dat een installatie overleeft — en desgewenst kan wachten op een zakelijke gebeurtenis zoals inloggen voordat het de gebruiker routeert — met op iOS en de -API van Google Play in plaats van een externe dienst.

Kernpunten

  • Uitgestelde deep linking behoudt de bestemming van een link over een installatie heen, zodat een eerste start op het juiste scherm landt in plaats van op het beginscherm.
  • Firebase Dynamic Links is uitgefaseerd en op 25 augustus 2025 gestopt — de meest gebruikte oplossing is nu weg, en bestaande links lossen niet meer op.
  • De populaire zelfbouwalternatieven — device fingerprinting en klembordmatching — zijn onbetrouwbaar, privacygevoelig en worden door Apple ontmoedigd.
  • Op iOS vangt een lichte App Clip de link op voordat de volledige app bestaat en geeft hem na installatie door via een gedeelde App Group-container.
  • Op Android levert de officiële Play Install Referrer API de lading van de link bij de eerste start betrouwbaar af.
  • Een wachtrij met openstaande links laat je navigatie uitstellen tot zowel de installatie is afgerond als een eventuele zakelijke gebeurtenis (zoals een geslaagde login) heeft plaatsgevonden.

Wat is uitgestelde deep linking?

Een deep link opent een specifiek scherm in een app — bijvoorbeeld myapp://product/42. Een deep link werkt alleen als de app al geïnstalleerd is. Uitgestelde deep linking haalt die voorwaarde weg: is de app niet geïnstalleerd, dan wordt de bestemming onthouden door het winkelbezoek en de installatie heen, en navigeert de app er bij de eerste start naartoe.

Het lastige is het gat. Tussen de tik en de eerste start installeert het besturingssysteem een verse app die geen herinnering heeft aan waarop de gebruiker tikte. Een lading over dat gat dragen — betrouwbaar en zonder privacy te schenden — is het hele probleem.

Het grootste deel van het afgelopen decennium was Firebase Dynamic Links het standaardantwoord. Het regelde de doorverwijzing naar de winkel, de uitgestelde lading en het oplossen bij de eerste start voor beide platforms achter één SDK.

Google faseerde Dynamic Links uit en zette de dienst op 25 augustus 2025 volledig stop. Links losten niet meer op, en er is geen directe vervanger van Google. Betaalde attributieplatforms als Branch en AppsFlyer vullen het gat commercieel op, maar veel teams willen de stroom zelf in handen hebben zonder een rekening per gebeurtenis of zonder kliks van gebruikers naar een derde partij te sturen.

Firebase Dynamic Links is geen optie meer

Hangt je uitgestelde deep linking nog aan Firebase Dynamic Links, dan werkt hij sinds 25 augustus 2025 niet meer. Overstappen op Universal Links en Android App Links dekt het geval waarin de app wel geïnstalleerd is — maar niet het uitgestelde geval waarin dat nog niet zo is. Precies dat gat dicht deze aanpak.

De gangbare zelfbouwalternatieven — en hun valkuilen

Zodra Dynamic Links van tafel is, grijpen de meeste gidsen naar een van twee zelfgebouwde technieken. Beide zijn het waard te begrijpen, juist omdat ze broos zijn.

Device fingerprinting. De webpagina legt bij de tik een "vingerafdruk" vast (IP-adres, schermformaat, OS-versie, taalregio). Bij de eerste start stuurt de app zijn eigen vingerafdruk naar een server die de twee binnen een kort venster probeert te koppelen. De valkuilen zijn ernstig: gedeelde en mobiele IP-adressen leiden tot misgrepen, het koppelvenster is kort, de nauwkeurigheid daalt op drukke netwerken, en waarschijnlijkheidsmatching van gebruikers is precies het patroon waar de privacyregels van Apple tegenin duwen.

Klembord. De webpagina kopieert de lading naar het klembord; de app leest die bij het starten. Sinds iOS 14 levert dat een zichtbare melding "geplakt uit Safari" op, kunnen gebruikers het klembord wissen, en vernietigt elke andere kopieeractie ertussen de lading.

AanpakPlatformBetrouwbaarheidPrivacyStatus
Firebase Dynamic LinksiOS + AndroidHoogAanvaardbaarGestopt aug. 2025
Device fingerprintingiOS + AndroidLaag–gemiddeldSlechtOntmoedigd
KlembordmatchingiOSGemiddeldPlakmelding zichtbaarBroos
App Clip + App Group (de onze)iOSHoogGoedAanbevolen
Play Install Referrer APIAndroidHoogGoedOfficieel

Onze aanpak: App Clips op iOS, Install Referrer op Android

In plaats van na de installatie te gokken wie de gebruiker is, vangen we de link betrouwbaar op elk platform op met een eigen mechanisme, en komen we daarna in de app samen in één oplosstap.

Een App Clip is een klein stukje van je app (onder de 15 MB) dat vrijwel direct start vanaf een link, een App Clip Code of een QR-code — zonder volledige installatie. Precies die eigenschap hebben we nodig: de App Clip draait voordat de volledige app bestaat, en kan de oorspronkelijke link dus zien.

De stroom:

  1. De link opent de App Clip. iOS levert de aanroep-URL via een NSUserActivity.
  2. De App Clip schrijft die URL (plus een tijdstempel) in een gedeelde App Group-container die de volledige app ook kan lezen.
  3. De App Clip toont een App Store-overlay die de gebruiker vraagt de volledige app te installeren.
  4. Na de installatie leest de volledige app bij de eerste start de openstaande link uit diezelfde App Group-container.
// App Clip — leg de aanroep-URL vast in de gedeelde App Group
func scene(_ scene: UIScene, continue activity: NSUserActivity) {
    guard activity.activityType == NSUserActivityTypeBrowsingWeb,
          let url = activity.webpageURL else { return }

    let shared = UserDefaults(suiteName: "group.pro.nerdy.deeplink")
    shared?.set(url.absoluteString, forKey: "pendingDeepLink")
    shared?.set(Date(), forKey: "pendingDeepLinkAt")
}
// Volledige app — lees hem één keer, bij de eerste start na installatie
let shared = UserDefaults(suiteName: "group.pro.nerdy.deeplink")
if let link = shared?.string(forKey: "pendingDeepLink") {
    DeepLinkQueue.shared.enqueue(link)
    shared?.removeObject(forKey: "pendingDeepLink")
}

Doordat de App Clip en de volledige app dezelfde App Group delen, wordt de lading exact overgedragen — geen vingerafdrukken koppelen, geen klembordmelding, geen gegok op waarschijnlijkheid.

Android: de Install Referrer API van Google Play

Android heeft een net, officieel antwoord: de Play Install Referrer API. Hang je lading als parameter referrer aan de Play Store-URL, en Google levert precies die tekenreeks bij de eerste start aan de app.

https://play.google.com/store/apps/details?id=pro.nerdy.app&referrer=deeplink%3D%2Fproduct%2F42
val client = InstallReferrerClient.newBuilder(context).build()
client.startConnection(object : InstallReferrerStateListener {
    override fun onInstallReferrerSetupFinished(responseCode: Int) {
        if (responseCode == InstallReferrerClient.InstallReferrerResponse.OK) {
            val referrer = client.installReferrer.installReferrer // "deeplink=/product/42"
            DeepLinkQueue.enqueue(referrer)
            client.endConnection()
        }
    }
    override fun onInstallReferrerServiceDisconnected() {}
})

Dit is betrouwbaar — de referrer-tekenreeks overleeft het winkelbezoek en de installatie onaangeroerd, zonder dat er aan de serverkant iets gekoppeld hoeft te worden.

Samenkomen op één plek — en wachten op de login

Beide platforms voeden nu dezelfde wachtrij met openstaande links in de app. We bouwden precies dit voor ons cashback- en loyaliteitsplatform, en we doen het in ons werk aan Flutter-appontwikkeling met een klein platform channel dat de native lading aan Dart doorgeeft, waarna één oplosser bepaalt wanneer er gehandeld wordt.

Dat "wanneer" telt. Een deep link naar een scherm achter een login (/orders/42) faalt bij een koude start als de gebruiker nog niet is ingelogd. De oplosser navigeert dus niet meteen — hij houdt de bestemming vast tot de app klaar is en elke vereiste zakelijke gebeurtenis heeft plaatsgevonden.

class DeepLinkQueue {
  Uri? _pending;
  bool _authReady = false;

  void enqueue(Uri link) { _pending = link; _tryResolve(); }

  // Aangeroepen door de businesslaag, bijvoorbeeld na een geslaagde login
  void onEvent(AppEvent event) {
    if (event == AppEvent.loggedIn) _authReady = true;
    _tryResolve();
  }

  void _tryResolve() {
    final link = _pending;
    if (link == null || !_authReady) return; // wait for both
    _pending = null;
    router.go(link.path); // safe: app is installed AND the user is logged in
  }
}

Het resultaat is een uitgestelde deep link die bestand is tegen de twee faalwijzen die de meeste implementaties breken: de app die niet geïnstalleerd is, en het doelscherm dat nog niet klaar is.

Wanneer deze aanpak past

Dit patroon komt tot zijn recht wanneer je zowel de bron van de link als de app in de hand hebt, betrouwbaarheid in eigen beheer wilt, en om privacy of leverancierskosten geeft. Heb je alleen het geval nodig waarin de app wel geïnstalleerd is, dan volstaan gewone Universal Links en App Links. Heb je marketingattributie over meerdere kanalen met dashboards nodig, dan kan een betaald platform als Branch de uitgave waard zijn. Maar voor productstromen — uitnodigingen, gedeelde content, onboarding, wachtwoordherstel — geven App Clips plus de Install Referrer API je een betrouwbare uitgestelde deep link die je volledig zelf bezit.

Stap je over van Firebase Dynamic Links of bouw je dit vanaf nul, dan helpen we je het op te leveren.

Veelgestelde vragen

Uitgestelde deep linking behoudt het doel van een link over een app-installatie heen. Tikt een gebruiker op een link zonder de app geïnstalleerd te hebben, dan wordt de bestemming onthouden, installeert de gebruiker de app, en routeert de app bij de eerste start naar het oorspronkelijke doel in plaats van naar een generiek beginscherm.
Google faseerde Firebase Dynamic Links uit en zette de dienst op 25 augustus 2025 stop. Bestaande links losten niet meer op, dus elk product dat erop leunde heeft een vervanger voor uitgestelde deep linking nodig.
Een App Clip start direct vanaf een link zonder volledige installatie, vangt de aanroep-URL op, en schrijft die naar een gedeelde App Group-container. Nadat de gebruiker de volledige app heeft geïnstalleerd, leest de app die opgeslagen link bij de eerste start en brengt hij de gebruiker naar de oorspronkelijke bestemming.
Ja. De Install Referrer API is een officiële API van Google die de referrer-tekenreeks van een Play Store-link bij de eerste start aan de app aflevert. Ze is betrouwbaar, anders dan device fingerprinting, dus de lading van de deep link komt intact aan.
Ja. Bewaar de opgeloste link in een wachtrij en voer de navigatie pas uit nadat de vereiste zakelijke gebeurtenis heeft plaatsgevonden, zoals een geslaagde login. Zo kun je gebruikers naar schermen achter een login sturen die bij een koude start anders zouden falen.