guides

Firebase Dynamic Links Migration Guide: Step-by-Step for iOS, Android, and React Native

Migrate from Firebase Dynamic Links to WarpLink in 30 minutes. Step-by-step code examples for iOS, Android, and React Native.

WarpLink Team··18 min read

TL;DR: Firebase Dynamic Links was deprecated in August 2025. This guide walks you through migrating to WarpLink in about 30 minutes. You will create a WarpLink account, register your app, install the SDK, replace link creation and handling code, and verify everything works. Every code example shows the Firebase pattern you are replacing alongside the WarpLink equivalent.

Why Migrate Now

Google officially deprecated Firebase Dynamic Links in August 2025. If you are still using FDL, here is what that means in practice:

  • No new features or bug fixes. The service is in maintenance-only mode. Any issues with iOS or Android OS updates will not be patched.
  • Existing links continue to work for now. Google has not announced a hard shutdown date, but the official FAQ recommends migrating as soon as possible.
  • SDK updates have stopped. The Firebase Dynamic Links SDK will not receive compatibility updates for new Xcode, Android SDK, or React Native versions.
  • Console access is limited. You can still view existing links, but creation of new Dynamic Links through the Firebase Console may be restricted.

The longer you wait, the more existing links accumulate in emails, social posts, and campaigns that will eventually need updating. Migrating now gives you time to redirect old links gracefully rather than scrambling after a hard deadline.

WarpLink is built specifically for this migration. It covers every Firebase Dynamic Links feature (deep links, deferred deep links, social previews, analytics) and adds sub-10ms edge redirects, install attribution, and an MCP server for AI agents. The free tier covers 10,000 clicks per month with no credit card required.

What You Will Need

Before starting, gather these items:

  • A WarpLink account (sign up at warplink.app)
  • Your Firebase project configuration:
    • iOS: Bundle ID, Apple Team ID, App Store URL
    • Android: Package name, SHA-256 certificate fingerprints, Play Store URL
  • Access to your app's source code (Xcode project, Android project, or React Native project)
  • Your existing Firebase Dynamic Links patterns (short link domain, link prefixes, deep link URL schemes)

If you have both iOS and Android (or a React Native app), you will register them as a single app in WarpLink. One app registration covers all platforms.

Migration Overview

The full migration has six steps:

  1. Set up WarpLink. Create your account, organization, and app registration.
  2. Install the SDK. Add the WarpLink SDK to your iOS, Android, or React Native project.
  3. Replace link creation. Swap Firebase DynamicLinkComponents with WarpLink's API or dashboard.
  4. Replace link handling. Update your deep link handlers for cold start, warm start, and deferred deep links.
  5. Migrate existing links. Redirect old Firebase short URLs and update active campaigns.
  6. Verify and test. Confirm deep links work end-to-end on real devices.

Each step includes side-by-side code examples showing the Firebase code you are removing and the WarpLink code replacing it.

Create Your Account and Organization

  1. Go to warplink.app and sign up with email, GitHub, or Google.
  2. Create an organization. This is the container for your apps, links, team members, and billing.
  3. You will land on the dashboard. From here, navigate to Apps to register your app.

Register Your App

Click Create App and fill in your platform details. Here is how Firebase fields map to WarpLink:

Firebase Console FieldWarpLink Field
iOS Bundle IDiOS Bundle Identifier
App Store ID / URLiOS App Store URL
Team IDiOS Team ID
Android Package NameAndroid Package Name
SHA-256 Certificate FingerprintAndroid SHA-256 Fingerprints
Play Store linkAndroid Play Store URL

Key difference from Firebase: WarpLink automatically generates and hosts your apple-app-site-association (AASA) and assetlinks.json files. You do not need to manually create or upload these files. When you save your app configuration, WarpLink builds the well-known files and serves them from your link domain.

If your app supports both iOS and Android, enter both platform details in the same app registration. React Native apps should register both platforms as well.

In your app registration, specify the path patterns your app should handle. These correspond to the associatedDomains paths in your iOS entitlements and the pathPatterns in your Android manifest.

For most apps, the default pattern (/\*) works. If you need specific patterns, add them during app registration. WarpLink includes these patterns in the generated AASA and assetlinks files.

Step 2: Install the SDK

iOS (Swift Package Manager)

In Xcode, go to File > Add Package Dependencies and add:

https://github.com/WarpLinkApp/warplink-ios-sdk

Select the latest version and add the WarpLink library to your app target.

Initialize the SDK in your AppDelegate or App struct:

// Before (Firebase)
import FirebaseDynamicLinks

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

// After (WarpLink)
import WarpLink

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        WarpLink.configure(apiKey: "wl_live_your_api_key")
        return true
    }
}

Android (Maven Central)

Add the dependency to your module-level build.gradle.kts:

// Before (Firebase)
implementation("com.google.firebase:firebase-dynamic-links-ktx:21.2.0")

// After (WarpLink)
implementation("app.warplink:sdk:<latest>")

Initialize in your Application class:

// Before (Firebase)
import com.google.firebase.FirebaseApp

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        FirebaseApp.initializeApp(this)
    }
}

// After (WarpLink)
import app.warplink.WarpLink

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        WarpLink.configure(this, apiKey = "wl_live_your_api_key")
    }
}

React Native (npm)

# Remove Firebase Dynamic Links
npm uninstall @react-native-firebase/dynamic-links

# Install WarpLink
npm install @warplink/react-native

For iOS, run cd ios && pod install to link the native module.

Initialize in your app entry point:

// Before (Firebase)
import dynamicLinks from '@react-native-firebase/dynamic-links';

// After (WarpLink)
import { WarpLink } from '@warplink/react-native';

WarpLink.configure({ apiKey: 'wl_live_your_api_key' });

Update Your Entitlements and Manifest

iOS: Update your Associated Domains entitlement. Replace the Firebase domain with your WarpLink link domain:

// Before
applinks:your-app.page.link

// After
applinks:aplnk.to

If you use a custom domain, substitute your domain here.

Android: Update your AndroidManifest.xml intent filter. Replace the Firebase host with your WarpLink domain:

<!-- Before -->
<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:host="your-app.page.link" android:scheme="https"/>
</intent-filter>

<!-- After -->
<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:host="aplnk.to" android:scheme="https"/>
</intent-filter>

Firebase Dynamic Links used DynamicLinkComponents (iOS), FirebaseDynamicLinks.getInstance() (Android), or the REST API. WarpLink provides both a dashboard UI and a REST API for link creation.

Firebase (iOS):

guard let link = URL(string: "https://example.com/product/123") else { return }
let dynamicLinksDomainURIPrefix = "https://your-app.page.link"
let linkBuilder = DynamicLinkComponents(link: link, domainURIPrefix: dynamicLinksDomainURIPrefix)

linkBuilder?.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.example.app")
linkBuilder?.androidParameters = DynamicLinkAndroidParameters(packageName: "com.example.app")

linkBuilder?.shorten { shortURL, warnings, error in
    guard let shortURL = shortURL else { return }
    print("Short link: \(shortURL)")
}

WarpLink (API):

curl -X POST https://api.warplink.app/v1/links \
  -H "Authorization: Bearer wl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "destinationUrl": "https://example.com/product/123",
    "deepLinkPath": "/product/123",
    "appId": "your-app-id"
  }'

The response includes a short link (e.g., https://aplnk.to/abc123) that routes users to your app with the deep link path, falling back to the destination URL for users without the app.

Firebase used socialMetaTagParameters for Open Graph tags. WarpLink includes these directly in the link creation payload.

Firebase (iOS):

linkBuilder?.socialMetaTagParameters = DynamicLinkSocialMetaTagParameters()
linkBuilder?.socialMetaTagParameters?.title = "Check out this product"
linkBuilder?.socialMetaTagParameters?.descriptionText = "The best product you've ever seen"
linkBuilder?.socialMetaTagParameters?.imageURL = URL(string: "https://example.com/product.jpg")

WarpLink (API):

curl -X POST https://api.warplink.app/v1/links \
  -H "Authorization: Bearer wl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "destinationUrl": "https://example.com/product/123",
    "deepLinkPath": "/product/123",
    "appId": "your-app-id",
    "ogTitle": "Check out this product",
    "ogDescription": "The best product you have ever seen",
    "ogImageUrl": "https://example.com/product.jpg"
  }'

WarpLink automatically detects bot user agents (social media crawlers, messaging app previews) and serves a full HTML page with Open Graph and Twitter Card tags. Real users get an instant redirect.

Firebase used analyticalParameters for UTM tracking. WarpLink supports UTM parameters directly.

Firebase (iOS):

linkBuilder?.analyticsParameters = DynamicLinkGoogleAnalyticsParameters()
linkBuilder?.analyticsParameters?.source = "twitter"
linkBuilder?.analyticsParameters?.medium = "social"
linkBuilder?.analyticsParameters?.campaign = "summer_launch"

WarpLink (API):

curl -X POST https://api.warplink.app/v1/links \
  -H "Authorization: Bearer wl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "destinationUrl": "https://example.com/product/123",
    "deepLinkPath": "/product/123",
    "appId": "your-app-id",
    "utmSource": "twitter",
    "utmMedium": "social",
    "utmCampaign": "summer_launch"
  }'

You can also create links through the WarpLink dashboard without writing any code. The dashboard provides the same fields in a form UI with a live preview of the short link.

This is the most important step. Your app needs to handle incoming deep links for three scenarios: cold start (app not running), warm start (app in background), and deferred deep links (user installs the app after clicking a link).

iOS (Swift)

Cold start and warm start handling:

// Before (Firebase)
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    let handled = DynamicLinks.dynamicLinks().handleUniversalLink(userActivity.webpageURL!) { dynamicLink, error in
        guard let dynamicLink = dynamicLink, let url = dynamicLink.url else { return }
        self.handleDeepLink(url)
    }
    return handled
}

// After (WarpLink)
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    guard let url = userActivity.webpageURL else { return false }
    WarpLink.handleDeepLink(url) { result in
        switch result {
        case .success(let deepLink):
            self.navigate(to: deepLink.path, params: deepLink.queryParameters)
        case .failure(let error):
            print("Deep link error: \(error)")
        }
    }
    return true
}

Deferred deep link handling (post-install):

// Before (Firebase)
DynamicLinks.dynamicLinks().getDynamicLink { dynamicLink, error in
    guard let dynamicLink = dynamicLink, let url = dynamicLink.url else { return }
    self.handleDeepLink(url)
}

// After (WarpLink)
WarpLink.checkDeferredDeepLink { result in
    switch result {
    case .success(let deepLink):
        // User installed the app after clicking a link.
        // deepLink.path contains the original destination.
        self.navigate(to: deepLink.path, params: deepLink.queryParameters)
    case .failure:
        // No deferred deep link found. Normal app launch.
        break
    }
}

If you use SceneDelegate, handle the link in scene(_:continue:):

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard let url = userActivity.webpageURL else { return }
    WarpLink.handleDeepLink(url) { result in
        switch result {
        case .success(let deepLink):
            self.navigate(to: deepLink.path, params: deepLink.queryParameters)
        case .failure(let error):
            print("Deep link error: \(error)")
        }
    }
}

Android (Kotlin)

Cold start and warm start handling:

// Before (Firebase)
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        FirebaseDynamicLinks.getInstance()
            .getDynamicLink(intent)
            .addOnSuccessListener { pendingDynamicLinkData ->
                val deepLink = pendingDynamicLinkData?.link ?: return@addOnSuccessListener
                handleDeepLink(deepLink)
            }
            .addOnFailureListener { e ->
                Log.e("DynamicLinks", "Error getting dynamic link", e)
            }
    }
}

// After (WarpLink)
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        intent.data?.let { uri ->
            WarpLink.handleDeepLink(uri) { result ->
                result.onSuccess { deepLink ->
                    navigate(deepLink.path, deepLink.queryParameters)
                }.onFailure { error ->
                    Log.e("WarpLink", "Deep link error", error)
                }
            }
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        // Handle warm start (app already running)
        intent.data?.let { uri ->
            WarpLink.handleDeepLink(uri) { result ->
                result.onSuccess { deepLink ->
                    navigate(deepLink.path, deepLink.queryParameters)
                }
            }
        }
    }
}

Deferred deep link handling (post-install):

// Before (Firebase)
FirebaseDynamicLinks.getInstance()
    .getDynamicLink(intent)
    .addOnSuccessListener { data ->
        val link = data?.link
        if (link != null) handleDeepLink(link)
    }

// After (WarpLink)
WarpLink.checkDeferredDeepLink { result ->
    result.onSuccess { deepLink ->
        // User installed after clicking a WarpLink.
        navigate(deepLink.path, deepLink.queryParameters)
    }
    // result is null if no deferred deep link exists.
}

React Native (TypeScript)

// Before (Firebase)
import dynamicLinks from '@react-native-firebase/dynamic-links';

// Handle link when app is in foreground
const unsubscribe = dynamicLinks().onLink(link => {
  handleDeepLink(link.url);
});

// Handle link that opened the app (cold start)
const initialLink = await dynamicLinks().getInitialLink();
if (initialLink) {
  handleDeepLink(initialLink.url);
}

// After (WarpLink)
import { WarpLink } from '@warplink/react-native';

// Handle all deep links (cold start, warm start, deferred)
WarpLink.onDeepLink(deepLink => {
  navigate(deepLink.path, deepLink.queryParameters);
});

// Check for deferred deep link on first launch
const deferredLink = await WarpLink.checkDeferredDeepLink();
if (deferredLink) {
  navigate(deferredLink.path, deferredLink.queryParameters);
}

For more details on each SDK, see the full documentation: iOS SDK, Android SDK, React Native SDK.

The trickiest part of any migration is handling links that are already in the wild: shared in emails, embedded in social posts, printed on marketing materials, or saved in bookmarks.

If you control your Firebase Dynamic Links domain (e.g., your-app.page.link), you can set up redirects from the old domain to your new WarpLink short links. This is the cleanest approach because every existing link continues to work.

  1. For each existing Firebase short link, create the equivalent WarpLink link with the same deep link path and destination URL.
  2. Set up a redirect from the old Firebase URL to the new WarpLink URL using your DNS provider or a simple redirect service.
  3. Monitor traffic on both old and new links through WarpLink analytics to track migration progress.

For links you can still edit (email templates, CMS content, app store listings, website CTAs):

  1. Create the equivalent link in WarpLink.
  2. Replace the old page.link URL with the new aplnk.to URL.
  3. Keep a spreadsheet mapping old links to new links for reference.

Option 3: Run Both During Transition

You can run Firebase Dynamic Links and WarpLink simultaneously during the transition period. Both SDKs can coexist in your app. Process incoming links from both services, and gradually shift new link creation to WarpLink while existing Firebase links continue to resolve through Google's infrastructure.

// iOS: Handle both during transition
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    guard let url = userActivity.webpageURL else { return false }

    if url.host?.contains("page.link") == true {
        // Legacy Firebase link
        return DynamicLinks.dynamicLinks().handleUniversalLink(url) { link, error in
            guard let link = link, let deepLink = link.url else { return }
            self.handleDeepLink(deepLink)
        }
    } else {
        // WarpLink
        WarpLink.handleDeepLink(url) { result in
            if case .success(let deepLink) = result {
                self.navigate(to: deepLink.path, params: deepLink.queryParameters)
            }
        }
        return true
    }
}

Once traffic on Firebase links drops to zero, remove the Firebase SDK entirely.

Step 6: Verify and Test

After completing the code changes, test every deep link scenario on real devices. Simulators and emulators do not fully support Universal Links and App Links.

iOS Testing

  1. AASA file verification. Open https://aplnk.to/.well-known/apple-app-site-association in a browser and confirm your bundle ID and paths are listed.
  2. Universal Link test. Paste a WarpLink URL into the Notes app and long-press it. You should see "Open in [Your App]" in the context menu.
  3. Safari test. Type or paste the WarpLink URL into Safari's address bar. It should open your app directly.
  4. Cold start test. Force-quit your app, then tap a WarpLink URL from Messages or Notes. The app should launch and navigate to the correct screen.
  5. Deferred deep link test. Delete your app, tap a WarpLink URL (it should redirect to the App Store or your fallback URL), reinstall the app, and launch it. The deferred deep link callback should fire with the original link data.

Tip: If Universal Links are not working, check Settings > Developer > Associated Domains Diagnostics on your test device. Also verify that your Associated Domains entitlement includes the correct domain.

Android Testing

  1. Assetlinks verification. Open https://aplnk.to/.well-known/assetlinks.json in a browser and confirm your package name and SHA-256 fingerprint are listed.
  2. ADB test. Run the following command to simulate a link tap:
adb shell am start -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d "https://aplnk.to/your-slug"
  1. Cold start test. Force-stop your app, then tap a WarpLink URL from a messaging app. The app should launch and navigate correctly.
  2. App chooser. If Android shows a disambiguation dialog ("Open with..."), your assetlinks.json may not be verified. Check that the SHA-256 fingerprint matches your signing key.

React Native Testing

Follow the iOS and Android steps above for each platform. Additionally:

  1. Verify that the native module is linked correctly by checking that WarpLink.configure() does not throw an error on app launch.
  2. Test that the onDeepLink callback fires for both iOS and Android deep links.
  3. Test deferred deep links on both platforms separately.

Common Test Issues

SymptomLikely CauseFix
Link opens in browser instead of appAASA/assetlinks not verifiedCheck domain in Associated Domains entitlement; verify AASA JSON is served with correct content type
App opens but navigates to home screenDeep link handler not processing the pathAdd logging in your deep link callback to confirm the path is received
Deferred deep link does not fireSDK not initialized before checkCall WarpLink.configure() before checkDeferredDeepLink()
Android disambiguation dialog appearsDigital Asset Links not verifiedVerify SHA-256 fingerprint matches your signing certificate
Link works on Wi-Fi but not cellularCDN caching delayWait a few minutes for AASA/assetlinks propagation; force-refresh on device

This table maps every Firebase Dynamic Links feature to its WarpLink equivalent.

Firebase Dynamic LinksWarpLinkNotes
Short links (page.link/xyz)Short links (aplnk.to/xyz)Custom domains available on all paid plans
DynamicLinkComponents (iOS)REST API or dashboardAPI returns short link URL; SDKs handle resolution
FirebaseDynamicLinks.getInstance() (Android)REST API or dashboardSame API works for all platforms
socialMetaTagParametersogTitle, ogDescription, ogImageUrlBot detection serves OG tags automatically
analyticsParameters (UTM)utmSource, utmMedium, utmCampaignTracked in WarpLink analytics dashboard
handleUniversalLink()WarpLink.handleDeepLink()Static method, takes URL directly
getDynamicLink(intent)WarpLink.handleDeepLink(uri)Takes Uri from intent.data
Deferred deep linkscheckDeferredDeepLink()Fingerprint-based matching with IDFV fallback
Firebase Console link analyticsWarpLink analytics dashboardReal-time with click deduplication
Custom domainsCustom domainsAvailable on all paid plans
apple-app-site-association (manual)Auto-generated AASARegenerated on app config changes
assetlinks.json (manual)Auto-generated assetlinksRegenerated on app config changes
Link previewsSocial previews (OG + Twitter Card)Automatic bot detection for rich previews
Install attributionMulti-strategy attributionReferrer, IDFV, fingerprint cascade
REST APIREST API (api.warplink.app/v1)Full CRUD for links, apps, analytics
No MCP/AI supportMCP serverFree on all plans. AI agents can create and manage links.

Common Migration Questions

Your existing Firebase short links will continue to resolve through Google's infrastructure as long as the service remains active. Google has not announced a hard shutdown date. During the transition, you can run both services simultaneously. For long-term reliability, set up redirects from your Firebase domain to your new WarpLink links.

Do I need to update my AASA file manually?

No. WarpLink generates and hosts your apple-app-site-association file automatically based on your app registration. When you register your iOS app with your bundle ID, team ID, and path patterns, WarpLink builds the AASA file and serves it from your link domain. Any changes to your app configuration trigger automatic regeneration.

Yes. Both SDKs can coexist in your app. Route incoming Universal Links based on the URL host: Firebase links go to the Firebase handler, WarpLink links go to the WarpLink handler. See the code example in Step 5 above. This lets you migrate gradually without a hard cutover.

What about analytics continuity?

WarpLink analytics start from the moment you create your first link. Historical Firebase analytics remain in the Firebase Console. There is no way to import Firebase analytics into WarpLink because the data models are different. For reporting continuity, note your migration date and reference both dashboards for the overlap period.

Can I use my existing custom domain?

Yes. If you were using a custom domain with Firebase Dynamic Links (instead of the default page.link), you can configure the same domain in WarpLink. Add the domain in the WarpLink dashboard under Settings > Custom Domains, update your DNS records as instructed, and WarpLink will serve links and well-known files from that domain.

Firebase Dynamic Links used a combination of clipboard-based and fingerprint-based attribution. WarpLink uses a multi-strategy cascade: referrer matching (most accurate), device ID matching via IDFV (deterministic on iOS), and probabilistic fingerprinting as a fallback. The SDK does not use IDFA or require App Tracking Transparency prompts. For more on how deep linking and attribution work, see The Complete Guide to Deep Linking.

What if I only need simple URL shortening?

WarpLink works as a smart URL shortener without any app registration. Create a link with just a destinationUrl and you get a short link with click analytics, social previews, and UTM tracking. You can add deep linking later by registering an app and attaching it to your links. See the Quickstart to get started in under five minutes.

Next Steps

You have migrated from Firebase Dynamic Links to WarpLink. Here is what to do next:

If you run into issues during migration, check the troubleshooting table in Step 6 or reach out through the dashboard.

WarpLink Team

Building the open deep linking platform for developers and small teams.