Migrate from Firebase Dynamic Links

Replace Firebase Dynamic Links with LinkTrail: map each FDL parameter, swap the handler on iOS, Android, React Native and Flutter, keep old URLs working.

Firebase Dynamic Links has shut down. This guide moves an app off it: what each FDL link parameter becomes, the code change on each platform, what happens to links already in the wild, and how to verify the cutover. The code change is small — one SDK swap and one handler — so most of the time goes into inventory and testing.

Before you start, finish the dashboard half of the Quickstart — register your apps, add a link domain, and create an API key. The background on why the shutdown matters is in the FDL replacement overview.

  • Every Dynamic Link your app, emails, ads, QR codes and printed material use — long links and short links.
  • The domain each one is on: the shared page.link / app.goo.gl domains, or a custom domain you own.
  • The in-app destination each one opens (the link parameter) and any query data your app reads from it.
  • The code that reads links: handleUniversalLink / dynamicLink(fromCustomSchemeURL:) on iOS, getDynamicLink(intent) on Android, getInitialLink / onLink in React Native and Flutter.

A LinkTrail link is created in the dashboard (Links → Create link). Most FDL parameters map onto one of its fields:

Firebase Dynamic LinksLinkTrail link
Domain (page.link or custom)Link domain — your *.linktrail.io subdomain or a custom domain
Short link suffixSlug (domain + slug are the URL, and are fixed once created)
link (the deep link URL)Deep link path (e.g. /products/aj1) plus custom JSON data for anything the query string carried
ibi / isi (iOS bundle ID, App Store ID)The iOS app you registered in Apps
apn (Android package name)The Android app you registered in Apps
ofl / ifl / afl (fallback links)Fallback URL
utm_source, utm_medium, utm_campaignCampaign / channel / UTM fields

Everything except the domain and slug stays editable after the link is created, so you can recreate links first and adjust destinations during testing.

3. Swap the SDK

FDL gave you the link in several places — a Universal Link handler, a custom-scheme handler, and a separate call for the first launch after install. LinkTrail delivers every case, deferred and re-engagement, through one onLink callback. Remove the Firebase Dynamic Links dependency, install the LinkTrail SDK for your platform, and replace the handler.

iOS (Swift)

// Before — Firebase Dynamic Links
DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicLink, _ in
    if let deep = dynamicLink?.url { router.route(to: deep.path) }
}

// After — LinkTrail: configure once at launch, then one hook
try LinkTrail.configure(apiKey: "lt_live_...")
LinkTrail.shared?.onLink { link, source in
    router.route(to: link.path, customData: link.customData)
}

// Keep forwarding incoming URLs, now to LinkTrail:
.onOpenURL { LinkTrail.shared?.handleDeepLink($0) }

Replace the Associated Domains entry for your page.link domain with applinks:yourapp.linktrail.io (or your custom link domain). LinkTrail hosts the AASA file for you. On iOS, deferred links rely on a click token read through a paste control by default — see iOS SDK → Deferred attribution.

Android (Kotlin)

// Before — Firebase Dynamic Links
Firebase.dynamicLinks.getDynamicLink(intent).addOnSuccessListener { data ->
    data?.link?.let { router.route(it.path) }
}

// After — LinkTrail, in Application.onCreate()
LinkTrail.configure(context = this, apiKey = "lt_live_...")
LinkTrail.shared?.onLink { link, source ->
    router.route(link.path, link.customData)
}

// In your Activity (onCreate and onNewIntent):
LinkTrail.shared?.handleDeepLink(intent?.data)

Point the android:autoVerify intent filter at your LinkTrail link domain instead of the page.link host, and register every SHA-256 signing fingerprint in the dashboard — see App Credentials. Deferred links come from the Play Install Referrer, with no extra setup.

React Native

// Before — @react-native-firebase/dynamic-links
const initial = await dynamicLinks().getInitialLink();
if (initial) router.navigate(new URL(initial.url).pathname);
const unsubscribe = dynamicLinks().onLink((link) => router.navigate(new URL(link.url).pathname));

// After — linktrail-react-native
await LinkTrail.configure('lt_live_...');
LinkTrail.onLink((link, source) => {
  router.navigate(link.path, link.customData);
});

One listener replaces both getInitialLink and onLink. Update the associated domains and intent filters as above — the React Native SDK page has the Expo config.

Flutter

// Before — firebase_dynamic_links
final initial = await FirebaseDynamicLinks.instance.getInitialLink();
if (initial != null) router.route(initial.link.path);
FirebaseDynamicLinks.instance.onLink.listen((data) => router.route(data.link.path));

// After — linktrail_flutter
LinkTrail.onLink.listen((event) {
  router.route(event.link.path, event.link.customData);
});
await LinkTrail.configure(apiKey: 'lt_live_...');

Subscribe to onLink before calling configure so the first-launch link isn't missed. Platform setup is on the Flutter SDK page.

  • Custom domain: if your FDL links ran on a domain you own, add that domain in Domains and repoint its DNS. Recreate each link with the same slug, or map old paths to new ones, so URLs already printed or emailed keep resolving — now through LinkTrail.
  • page.link / app.goo.gl: those domains belong to Google and stopped resolving at shutdown. They can't be moved; update every surface you still control to the new links.
  • Have a large set of links to move? Talk to us — we help migrate existing links as part of onboarding.

5. Verify on a clean install

# The association files LinkTrail hosts for your domain:
curl -sI https://yourapp.linktrail.io/.well-known/apple-app-site-association | grep -i content-type
curl -s https://yourapp.linktrail.io/.well-known/assetlinks.json

# Android: re-run App Links verification for your package
adb shell pm verify-app-links --re-verify com.example.app
  1. 1Uninstall the app, tap a new link, install from the store, and confirm the first open lands on the linked screen (the deferred case).
  2. 2With the app installed, tap the link from Notes or Messages and confirm it opens the app, not the browser (the direct case).
  3. 3Check both platforms with the free Universal Links validator before switching campaigns over.

If links open the browser instead of the app after the switch, the usual causes are a stale Associated Domains entry, a missing signing fingerprint, or iOS caching the old AASA — see Troubleshooting.