10

I've been working to include Dynamic Links in my app. I have set it up so that links can be generated correctly, and received correctly, but they only work when the app is open and in the background. If the app is closed completely, the link will just open the app. It looks like it is not calling the continueUserActivity method in the AppDelegate even though I am returning TRUE in the didFinishLaunchingWithOptions method. I have read that I should be getting the incoming URL in the didFinishLaunchingWithOptions method, but the available answers do not work and are outdated, so I was hoping someone would know.

Here's the code:

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate{

var window: UIWindow?


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    FirebaseApp.configure()
    return true
}

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

func handleIncomingDynamicLink(_ dynamicLink: DynamicLink) {
    guard let url = dynamicLink.url else {
        print("No incoming link")
        return
    }
    print("Link is: \(url)")
    SystemManager.sharedInstance.setDeepLinkTrip(tripKey: deepLinkParser(link: url))
    
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let initialViewController = storyboard.instantiateViewController(withIdentifier: "RootVC")
    self.window?.rootViewController = initialViewController
}

So how can I make it so that the link is properly opened when the app is starting up cold these days? Thanks for all help :)

EDIT: I have updated my app to use SceneDelegate, which all other answers suggested, but it STILL does not work. It will still open dynamic links if running in the background, but otherwise it will not open them. It doesn't look like any link is ever sent to the app. I can't find any userActivity, urlContexts, etc.

Heres new relevant code:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

//I read that this function should be handling the link when it is clicked while the app is dead, but nothing works. Basically this checks that the user is logged in (always is), then looks for a link.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    
    self.scene(scene, openURLContexts: connectionOptions.urlContexts)
    
    guard let _ = (scene as? UIWindowScene) else { return }

    if Auth.auth().currentUser != nil {
        
        if let userActivity = connectionOptions.userActivities.first {
          self.scene(scene, continue: userActivity)
        } else {
          self.scene(scene, openURLContexts: connectionOptions.urlContexts)
        }
        
        if let userActivity = connectionOptions.userActivities.first {
            if let incomingURL = userActivity.webpageURL {
                _ = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
                    guard error == nil else { return }
                    if let dynamicLink = dynamicLink {
                        self.handleIncomingDynamicLink(dynamicLink)
                    }  else {
                       print("Something went wrong")
                       let storyboard = UIStoryboard(name: "Main", bundle: nil)
                       let initialViewController = storyboard.instantiateViewController(withIdentifier: "RootVC")
                       self.window?.rootViewController = initialViewController
                   }
                }
            }
        } else {

            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let initialViewController = storyboard.instantiateViewController(withIdentifier: "RootVC")
            self.window?.rootViewController = initialViewController
        }
    }
    
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        
    if let url = URLContexts.first?.url {
        _ = DynamicLinks.dynamicLinks().handleUniversalLink(url) { (dynamicLink, error) in
        guard error == nil else { return }
        if let dynamicLink = dynamicLink {
        //your code for handling the dynamic link goes here
            self.handleIncomingDynamicLink(dynamicLink)
            }
        }
    }
}

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let incomingURL = userActivity.webpageURL {
        
        _ = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
            guard error == nil else {
                print(error!.localizedDescription)
                return
            }
            if let dynamicLink = dynamicLink {
                self.handleIncomingDynamicLink(dynamicLink)
            } else {
                print("Soemthing went wrong")
            }
        }
    }
}
Cody Lucas
  • 682
  • 1
  • 6
  • 23

3 Answers3

3

I figured out what was wrong. The issue was unrelated to SceneDelegate - the way that I handled the link was being overwritten by another network call.

How I fixed this, since you cannot see print statements when opening an app from a cold start, is by saving a message to UserDefaults, and then creating a popup alert to read the saved message once the app loads.

In the end, I did need to move to SceneDelegate over AppDelegate, though.

Cody Lucas
  • 682
  • 1
  • 6
  • 23
1

Works for:

  • app is in background or foreground

  • app is installed but close

  • app is not installed, click on Firebase Dynamiclink, download from AppleStore, open App

    import UIKit
    import Firebase
    import FirebaseDynamicLinks
    
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
      //Call when app is close
      func scene(_ scene: UIScene,
                 willConnectTo session: UISceneSession,
                 options connectionOptions: UIScene.ConnectionOptions) {
    
          if let userActivity = connectionOptions.userActivities.first {
              if let incomingURL = userActivity.webpageURL {
                  handleFirebaseDynamicLink(url: incomingURL)
              }
          }
      }
    
      // Call for a launched app after download from AppleStore
      func scene(_ scene: UIScene,
                 openURLContexts URLContexts: Set<UIOpenURLContext>) {
          if let schemeURL = URLContexts.first?.url {
              handleFirebaseDynamicLinksFromScheme(schemeURL)
          }
      }
    
      // Call while runing
      func scene(_ scene: UIScene,
                 continue userActivity: NSUserActivity) {
          guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
                let url = userActivity.webpageURL else {
              return
          }
          handleFirebaseDynamicLink(url: url)
      }
    
    }
    
    // MARK: Private
    
    extension SceneDelegate {
    
      private func handleFirebaseDynamicLink(url: URL) {
          DynamicLinks.dynamicLinks()
              .handleUniversalLink(url) { dynamiclink, error in
                  if let error = error {
                      return
                  }
                  guard let dynamiclink = dynamiclink else {
                      return
                  }
                  guard let url = dynamiclink.url else {
                      return
                  }
                  self.handle(url: url)
              }
      }
    
      private func handleFirebaseDynamicLinksFromScheme(_ url: URL) {
          guard let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url),
             let url = dynamicLink.url else {
              return
          }
          handle(url: url)
      }
    
alegelos
  • 2,308
  • 17
  • 26
-1

I don't have the possibility to test this, but it could help to handle the dynamic link when the application is closed.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    if let userActivityDict = launchOptions?[.userActivityDictionary] as? [UIApplication.LaunchOptionsKey : Any],
        let userActivity = userActivityDict[.userActivityType] as? NSUserActivity {

        // execute the dynamic url code here using the userActivity
    }

    return true
}
lubilis
  • 3,942
  • 4
  • 31
  • 54