Dynamic Links Firebase function not being called at all Xcode 12?

Viewed 1129

So I have a dynamic link that is working in that it opens up the app when I click on it but the handling of the dynamic link doesn't happen. This is because application function seen below is never entered and I'm not sure why...

func handleIncomingDynamicLink(_ dynamicLink: DynamicLink){
    guard let url = dynamicLink.url else {
        print("That's weird. My dynamic link object has no url")
        return
    }
    print("Your incoming link parameter is \(url.absoluteString)")
}

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    if let incomingURL = userActivity.webpageURL {
        print("Incoming URL is \(incomingURL)")
        let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
            guard error == nil else{
                print("Found an error! \(error!.localizedDescription)")
                return
            }
            if let dynamicLink = dynamicLink {
                self.handleIncomingDynamicLink(dynamicLink)
            }
        }
        if linkHandled {
            return true
        } else {
            return false
        }
    }
    return false
}

Any help would be greatly appreciated--is it possible this is a problem with Firebase itself or no?

3 Answers

SwiftUI 2.0 / @main App For the new SwiftUI 2.0 you can make an app only using SwiftUI. This means you don't use AppDelegate or SceneDelegate. You then have to use .onOpenURL to get any action called when you click on your link. I am usling this for Firebase Dynamic Links.

WindowGroup {
  ContentView()
    .onOpenURL { url in
      // handle the URL that must be opened
      let incomingURL = url
      print("Incoming URL is: \(incomingURL)")
      let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
          guard error == nil else {
             print("Error found!: \(error!.localizedDescription)")
                return
          }
          if let dynamicLink = dynamicLink {
             self.handleDynamicLink(dynamicLink)
          }
       }
       if linkHandled{
          print("Link Handled")
          return
       }else{
          print("NO linkHandled")
          return
       }
    }
}

Configuring Firebase Dynamic Links on iOS has several steps that must be followed. So, I recommend referring to the official documentation here. Additionally, there is a Firecasts series that walks through all of the steps here.

I followed the Firecasts series, but my setup is slightly different; so, I ran into a couple of issues. First, I am using a custom domain. Second, I'm using SwiftUI. I'll just address the issues that I had, so please refer to the full Firecasts series for all of the gruesome details.

SwiftUI 2.0

There are two different types of links that your app needs to handle. First, your app needs to handle links when your app is already installed (i.e. https://app.yourcustomdomain.com/...). Second, your app needs to handle links copied into the clipboard before your app was installed (i.e. com.your.bundle.id://google/link/...).

Also, you probably want to handle links originating from a web browser (Safari). In my case, I process a link from a QR code scan. Apparently NFC tag scan links also originate from Safari, but I can't confirm this.

After some trial and error, I found a way to handle all of these concerns with SwiftUI; it's unclear if this is the officially recommended approach, so proceed with caution.

First, use onOpenURL(perform:) and .onContinueUserActivity(_,perform:):

var body: some Scene {
    WindowGroup {
        ContentView()
            .onContinueUserActivity(NSUserActivityTypeBrowsingWeb, perform: onWebBrowserActivity)
            .onOpenURL(perform: onOpenURL)
    }
}

Second, define the onContinueUserActivity handler:

// Handles links originating from the web browser
func onWebBrowserActivity(_ userActivity: NSUserActivity) {
    guard let url = userActivity.webpageURL else {
        return
    }
    onOpenURL(url)
}

Third, define the onOpenURL handler:

func onOpenURL(_ url: URL) {
    let dynamicLinks = DynamicLinks.dynamicLinks()
    if dynamicLinks.shouldHandleDynamicLink(fromCustomSchemeURL: url),
       let dynamicLink = dynamicLinks.dynamicLink(fromCustomSchemeURL: url) {
        handle(dynamicLink: dynamicLink)
    } else {
        handleUniversalLink(url: url)
    }
}

Both types of links mentioned above are passed into the onOpenURL handler, so this if statement handles the two cases.

Finally, (as others have mentioned) handle universal links:

func handleUniversalLink(url: URL) {
    let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicLink, error in
        if let error = error {
            print("Could not follow the dynamic link \(error.localizedDescription)")
            return
        }
        if let dynamicLink = dynamicLink {
            self.handle(dynamicLink: dynamicLink)
        }
    }
    if linkHandled {
        print("Link handled")
    } else {
        print("Link not handled")
    }
}

Custom Domain

I had to follow the custom domain setup instructions here. Specifically, I had to set up Firebase Hosting, which requires adding an A DNS record (In addition to setting up a TXT DNS record for the Dynamic Links URL Prefix setup). Also, I had to specify the custom domain in my Info.plist

   <key>FirebaseDynamicLinksCustomDomains</key>
   <array>
           <string>https://app.yourcustomdomain.com</string>
   </array>

SwiftUI 1.0 / SceneDelegate

You do the same but in your SceneDelegate, following callback:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let incomingURL = userActivity.webpageURL {
        print("Incoming URL is \(incomingURL)")
        let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
            guard error == nil else{
                print("Found an error! \(error!.localizedDescription)")
                return
            }
            if let dynamicLink = dynamicLink {
                self.handleIncomingDynamicLink(dynamicLink)
            }
        }
}

SwiftUI 2.0 / WindowGroup

With SwiftUI 2.0 life-cycle you need to use instead .onContinueUserActivity, like

WindowGroup {
    ContentView()
     .onContinueUserActivity(<your_activity_type_here>) { userActivity in
         if let incomingURL = userActivity.webpageURL {
              print("Incoming URL is \(incomingURL)")
              let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
                    guard error == nil else{
                         print("Found an error! \(error!.localizedDescription)")
                         return
                    }
                    if let dynamicLink = dynamicLink {
                         self.handleIncomingDynamicLink(dynamicLink)
                    }
              }
        }
    }
}
Related