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:
- Configure your deep link domain on Dub. This replaces Firebase’s
*.page.linkdomain with your own branded deep link domain. - Create your deep links on Dub.
- Handle deep link redirects inside your app (more details below).
Migrating Android apps
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.DynamicLinkUpdate 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>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 the
/track/openendpoint - Retrieves the final destination URL
- Navigates to the destination URL in the app
Migrating iOS apps
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 FirebaseDynamicLinksUpdate 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:
- Open your Xcode project.
- Select your app target → Signing & Capabilities
- 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 the
/track/openendpoint - Retrieves the final destination URL
- Navigates to the destination URL in the app