Firebase Dynamic Links: Getting Started
Learn how to use Firebase Dynamic Links to implement deep linking on iOS. By Danijela Vrzan.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Firebase Dynamic Links: Getting Started
35 mins
- Getting Started
- Deep Linking
- Firebase Dynamic Links
- Setting Up Firebase
- Configuring Firebase Project Settings
- Setting Up the App Store ID
- Adding Your Team ID
- Setting Up a Domain for Hosting
- Setting Up Firebase Hosting
- Creating a Firebase-Hosted Subdomain
- Creating a Free Firebase Domain
- Adding Associated Domains in Xcode
- Testing Your Deep Link in Terminal
- Building Firebase Dynamic Links
- Defining the URL
- Using Dynamic Link Builder API
- Shortening the URL
- Seeing Your App in Action
- Interpreting Your Dynamic Link
- Handling the Incoming URL
- Parsing the URL Components
- Using Environment Values
- Handling the Incoming Dynamic Link
- Handling Navigation in Your App
- Where to Go From Here?
Interpreting Your Dynamic Link
Now comes the fun part! You’re going to direct your users to a specific screen within your app. When a user shares a link with a recipe, whoever receives it will see the detailed view of that same recipe if they have the app installed on their phone.
It sounds like magic, but it’s not. You simply need to know all the secret ingredients.
You’ll start by handling the URL in your app.
Handling the Incoming URL
With the new app life cycle in SwiftUI, incoming URLs are handled by calling .onOpenURL(perform:)
on your scene. Since you have a single scene, you attach it to that. But, if you had more scenes, you’d use the top-most scene since that’s where the navigation starts.
Open AppMain.swift. Below // Call onOpenURL
, add:
// 1
.onOpenURL { url in
print("Incoming URL parameter is: \(url)")
// 2
let linkHandled = DynamicLinks.dynamicLinks()
.handleUniversalLink(url) { dynamicLink, error in
guard error == nil else {
fatalError("Error handling the incoming dynamic link.")
}
// 3
if let dynamicLink = dynamicLink {
// Handle Dynamic Link
self.handleDynamicLink(dynamicLink)
}
}
// 4
if linkHandled {
print("Link Handled")
} else {
print("No Link Handled")
}
}
Here’s what’s happening:
-
.onOpenURL(perform:)
receives the incoming URL. -
handleUniversalLink(_:completion:)
parses the URL into aDynamicLink
. It makes a network call to convert the short dynamic link into a full dynamic link and extracts the URL parameter. It returns anError
if this fails. - You handle any dynamic link retrieved by calling a method that isn’t yet defined, which you’ll write next.
- Finally, you report on whether or not you handled the link. If you’re working with other universal links, this is where you’d handle those.
To resolve the compiler error, add the following under // Handle incoming dynamic link
:
func handleDynamicLink(_ dynamicLink: DynamicLink) {
}
You’ll flesh this out further in a later section.
Build and run. Select a recipe and tap Share.
Copy the short URL from your Xcode console and run the following command in the terminal window:
xcrun simctl openurl booted [your short URL]
Go back to Xcode and look at the console output:
The incoming URL and your shared dynamic link URL are the same.
Next, you’ll parse the URL parameter and extract the recipeID
.
Parsing the URL Components
Open DeepLinkHandler.swift. Add the following below // Parse url
:
func parseComponents(from url: URL) -> DeepLink? {
// 1
guard url.scheme == "https" else {
return nil
}
// 2
guard url.pathComponents.contains("about") else {
return .home
}
// 3
guard let query = url.query else {
return nil
}
// 4
let components = query.split(separator: ",").flatMap {
$0.split(separator: "=")
}
// 5
guard let idIndex = components.firstIndex(of: Substring("recipeID")) else {
return nil
}
// 6
guard idIndex + 1 < components.count else {
return nil
}
// 7
return .details(recipeID: String(components[idIndex + 1]))
}
Here, you:
- Make sure the URL has the right scheme. It's the same one you defined at the start.
- Check to see if the URL contains the about path component, or if you have your own website, the path you're using for the dynamic link. If it doesn't, it'll only open the app and show the home view, so it returns
.home
. - Make sure the incoming URL has a query string — the part saying recipeID=002.
- Now, you split the query string into its components. Using
flatMap(_:)
, you split each component separated by = and create a single array of elements. It looks like this:["recipeID", "002"]
. - Because the URL can have more query parameters, you need to find the
index
of recipeID and assign it toidIndex
. - The
components
array has two elements so theindex
of yourrecipeID
is1
. Check if it exists by making sure theidIndex + 1
is less than the number of components, which is two. - Finally, assign the
recipeID
value to.details(recipeID:)
and return it.
You've parsed your URL parameter and extracted the value of the recipeID
, but your app still doesn't know what to do with it. That's because you need to make sure the view that needs this value receives it.
You'll do that next using environment values.
Using Environment Values
It's often helpful to know your app's state. For example, perhaps you want to fetch new data when your app becomes active or remove any cached data once the app transitions to the background.
SwiftUI tracks a scene's state in the environment. You can make it available everywhere in your app by using the @Environment
property wrapper.
You'll use this approach and create your own EnvironmentKey
for deep links. If you want to learn more about Environment Values, check out Apple's Documentation on the topic.
Inside the Helpers folder, create a new Swift file called DeepLinkKey.swift.
Replace import Foundation
with:
import SwiftUI
Then add:
struct DeepLinkKey: EnvironmentKey {
static var defaultValue: DeepLinkHandler.DeepLink? {
return nil
}
}
Here, you declare a new environment key type and specify its required defaultValue
property as having a type of DeepLink?
.
Next, add the following extension to the bottom of the file:
// MARK: - Define a new environment value property
extension EnvironmentValues {
var deepLink: DeepLinkHandler.DeepLink? {
get {
self[DeepLinkKey]
}
set {
self[DeepLinkKey] = newValue
}
}
}
Here you create a custom environment value by extending EnvironmentValues
with the new property.
Now, open AppMain.swift and, below // Define deepLink
add:
@State var deepLink: DeepLinkHandler.DeepLink?
Then below // Add environment modifier
, add:
.environment(\.deepLink, deepLink)
Here, you've set the environment value for a view and all its subviews by calling .environment(_:_:)
on your HomeView()
.
Now that you defined your custom environment key, you can finish off handling the dynamic link.
Handling the Incoming Dynamic Link
Open AppMain.swift and, inside handleDynamicLink(_:)
, add the following:
guard let url = dynamicLink.url else { return }
print("Your incoming link parameter is \(url.absoluteString)")
// 1
guard
dynamicLink.matchType == .unique ||
dynamicLink.matchType == .default
else {
return
}
// 2
let deepLinkHandler = DeepLinkHandler()
guard let deepLink = deepLinkHandler.parseComponents(from: url) else {
return
}
self.deepLink = deepLink
print("Deep link: \(deepLink)")
// 3
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.deepLink = nil
}
Here's a code breakdown:
- Every dynamic link has a
matchType
that shows how confident the library is you've retrieved the dynamic link the user clicked. It has four types:unique
,default
,weak
andnone
. If the data you're sharing within the link is personal in nature, you'd want to make sure the match type isunique
. Otherwise, it's recommended that you don't show any personal information you can extract from the link as the link may have been used before. - You call
parseComponents(from:)
and pass the URL as an argument. If parsing was successful, you assign the returned value to yourdeepLink
environment value. - Since they remain available in your app's memory, you need to reset environment values. If the user clicks the same link again, nothing happens because the environment value hasn't changed.
Build and run. Select a recipe, then tap Share.
Copy the short URL from the Xcode console and run the following command in the terminal window:
xcrun simctl openurl booted [your short URL]
Go back to Xcode and look at the console output:
The deepLink
environment property's value is details(recipeID: "002")
.
The only thing left to do is use this value to navigate to a specific detailed view in your app.