Deep links require a Pro plan subscription or higher.
This guide walks you through replacing Firebase 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 to set up deep links on Dub by completing the following steps:
  1. Configure your deep link domain on Dub. This replaces Firebase’s *.page.link domain with your own branded deep link domain.
  2. Create your deep links on Dub.
  3. Handle deep link redirects inside your app (more details below).

Migrating Android apps

1

Remove Firebase Dynamic Links SDK

If you have the Firebase Dynamic Links SDK installed, remove it from your build.gradle file:
implementation 'com.google.firebase:firebase-dynamic-links:21.1.0'
Remove any Firebase imports from your Kotlin/Java files:
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks
import com.google.firebase.dynamiclinks.DynamicLink
2

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.
<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>
3

Implement Deep Link Handling

Override onNewIntent in your main activity to handle deep link opens:
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 Dub’s tracking endpoint https://api.dub.co/track/open
  • Retrieves the final destination URL
  • Navigates to the destination URL in the app

Migrating iOS apps

1

Remove Firebase Dynamic Links SDK

If you have the Firebase Dynamic Links SDK installed, remove it from your Podfile:
pod 'Firebase/DynamicLinks'
Remove any Firebase imports like:
import FirebaseDynamicLinks
2

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:
No changes to your app’s Info.plist are required unless you had other Firebase-specific deep link configurations.
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 Dub’s tracking endpoint https://api.dub.co/track/open
  • Retrieves the final destination URL
  • Navigates to the destination URL in the app