> ## Documentation Index
> Fetch the complete documentation index at: https://dub.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from Firebase Dynamic Links

> Learn how to migrate from Firebase Dynamic Links to Dub.

<Note>
  Deep links require a [Pro plan](https://dub.co/pricing) subscription or
  higher.
</Note>

This guide walks you through replacing [Firebase Dynamic Links](https://firebase.google.com/docs/dynamic-links) with Dub for deep link creation, click tracking, and routing across platforms.

## Prerequisites

Before you begin, make sure to follow the [quickstart guide](/docs/concepts/deep-links/quickstart) to set up deep links on Dub by completing the following steps:

1. Configure your [deep link domain](/docs/concepts/deep-links/quickstart#step-1-configure-your-deep-link-domains-on-dub) on Dub. This replaces Firebase's `*.page.link` domain with your own branded deep link domain.
2. [Create your deep links](/docs/concepts/deep-links/quickstart#step-2%3A-create-your-deep-links-on-dub) on Dub.
3. [Handle deep link redirects inside your app](/docs/concepts/deep-links/quickstart#step-3%3A-handling-deep-link-redirects-in-your-app) (more details below).

## Migrating Android apps

<Steps>
  <Step title="Remove Firebase Dynamic Links SDK">
    If you have the Firebase Dynamic Links SDK installed, remove it from your `build.gradle` file:

    ```gradle theme={null}
    implementation 'com.google.firebase:firebase-dynamic-links:21.1.0'
    ```

    Remove any Firebase imports from your Kotlin/Java files:

    ```java theme={null}
    import com.google.firebase.dynamiclinks.FirebaseDynamicLinks
    import com.google.firebase.dynamiclinks.DynamicLink
    ```
  </Step>

  <Step title="Update AndroidManifest.xml">
    If you’re using the same domain for both Firebase Dynamic Links and Dub (e.g., yourapp.com), no changes are needed — you can skip this section.

    However, if you were previously using Firebase’s branded domain (e.g., yourapp.page.link), you’ll need to replace it with your custom Dub domain in your app’s configuration.

    ```xml theme={null}
    <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="yourapp.page.link" /> <!-- Replace with your Dub deep link domain -->
      </intent-filter>
    </activity>
    ```
  </Step>

  <Step title="Implement Deep Link Handling">
    Override `onNewIntent` in your main activity to handle deep link opens:

    ```kotlin theme={null}
    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.data?.let { uri ->
            handleDeepLink(uri)
        }
    }

    private fun handleDeepLink(uri: Uri) {
        // Track the deep link open and get destination URL
        trackDeepLinkClick(uri.toString())
    }

    private fun trackDeepLinkClick(deepLink: String) {
        val url = URL("https://api.dub.co/track/open")
        val connection = url.openConnection() as HttpURLConnection

        connection.requestMethod = "POST"
        connection.setRequestProperty("Content-Type", "application/json")
        connection.doOutput = true

        val body = JSONObject().apply {
            put("deepLink", deepLink)
        }

        connection.outputStream.use { os ->
            os.write(body.toString().toByteArray())
        }

        val response = connection.inputStream.bufferedReader().use { it.readText() }
        val jsonResponse = JSONObject(response)
        val destinationUrl = jsonResponse.getString("url")

        runOnUiThread {
            navigateToDestination(destinationUrl)
        }
    }
    ```

    In a nutshell, this code:

    * Extracts the domain and key from the deep link URL
    * Sends the data to the [`/track/open` endpoint](/docs/api-reference/endpoint/track-open)
    * Retrieves the final destination URL
    * Navigates to the destination URL in the app
  </Step>
</Steps>

## Migrating iOS apps

<Steps>
  <Step title="Remove Firebase Dynamic Links SDK">
    If you have the Firebase Dynamic Links SDK installed, remove it from your `Podfile`:

    ```ruby theme={null}
    pod 'Firebase/DynamicLinks'
    ```

    Remove any Firebase imports like:

    ```swift theme={null}
    import FirebaseDynamicLinks
    ```
  </Step>

  <Step title="Update Associated Domains">
    If you're using the same domain for both Firebase Dynamic Links and Dub (e.g., yourapp.com), no changes are needed — you can skip this section.

    However, if you were using Firebase’s branded domain (e.g., yourapp.page.link), you’ll need to replace it with your Dub domain in your iOS project setup.

    To enable Universal Links with Dub, update your Associated Domains in Xcode:

    1. Open your Xcode project.
    2. Select your app target → **Signing & Capabilities**
    3. Under **Associated Domains**, replace the old Firebase domain with your Dub domain:

    <Warning>
      If your app uses other SDKs that handle URLs (e.g., Facebook/Meta SDK, Google Sign-In), make sure your `AppDelegate`'s `openURL` handler doesn't short-circuit with `||`. When a third-party SDK returns `true` first, Dub's `handleDubDeepLink` will never be called. See the [troubleshooting guide](/docs/concepts/deep-links/quickstart#troubleshooting-deep-links) for details and a fix.
    </Warning>

    No changes to your app’s `Info.plist` are required unless you had other Firebase-specific deep link configurations.

    ```swift theme={null}
    import Foundation
    import UIKit

    func handleDubDeepLink(\_ url: URL) {
    let domain = url.host ?? ""
    let key = url.lastPathComponent

    let payload: [String: Any] = [
      "domain": domain,
      "key": key,
    ]

    guard let requestURL = URL(string: "https://api.dub.co/track/open"),
      let httpBody = try? JSONSerialization.data(withJSONObject: payload) else {
        print("Invalid request setup")
        return
    }

    var request = URLRequest(url: requestURL)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = httpBody

    // Send the request
    URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            print("Failed to track deep link: \(error)")
            return
        }

        guard let data = data,
              let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let destination = json["url"] as? String else {
            print("Invalid response from /track/open")
            return
        }

        print("Resolved destination: \(destination)")

        DispatchQueue.main.async {
            // Navigate to the destination URL or handle it in-app
            navigateToScreen(from: destination)
        }
      }.resume()
    }


    func navigateToScreen(from url: String) {
      print("Navigating to in-app destination: \(url)")
      // Add your navigation logic here
    }
    ```

    In a nutshell, this code:

    * Sends the deep link URL to the [`/track/open` endpoint](/docs/api-reference/endpoint/track-open)
    * Retrieves the final destination URL
    * Navigates to the destination URL in the app
  </Step>
</Steps>
