Firebase Dynamic Links: Getting Started

Learn how to use Firebase Dynamic Links to implement deep linking on iOS. By Danijela Vrzan.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

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.

Funny image of a with brewing Swift coding magic with Swift logo

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:

  1. .onOpenURL(perform:) receives the incoming URL.
  2. handleUniversalLink(_:completion:) parses the URL into a DynamicLink. It makes a network call to convert the short dynamic link into a full dynamic link and extracts the URL parameter. It returns an Error if this fails.
  3. You handle any dynamic link retrieved by calling a method that isn’t yet defined, which you’ll write next.
  4. 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:

Xcode console output showing two print statement printing short dynamic link and the incoming URL and how they are the same

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:

  1. Make sure the URL has the right scheme. It's the same one you defined at the start.
  2. 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.
  3. Make sure the incoming URL has a query string — the part saying recipeID=002.
  4. 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"].
  5. Because the URL can have more query parameters, you need to find the index of recipeID and assign it to idIndex.
  6. The components array has two elements so the index of your recipeID is 1. Check if it exists by making sure the idIndex + 1 is less than the number of components, which is two.
  7. 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:

  1. 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 and none. If the data you're sharing within the link is personal in nature, you'd want to make sure the match type is unique. 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.
  2. You call parseComponents(from:) and pass the URL as an argument. If parsing was successful, you assign the returned value to your deepLink environment value.
  3. 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:

Xcode console printing the value of a deepLink environment property

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.