Search code examples
iosdeep-linkingfirebase-dynamic-linksuniversal-link

Firebase DeepLink does not work on Killed/Terminated App state in iOS14


I have integrated the Firebase DeepLink/DynamicLinks/UniversalLink into our app. When the app is running or in background/foreground state, Deep Links works with no problem. However, when the app is not running or in killed/terminated state the app will launch, but no trigger method will be called or initialize. It's like it only triggers the URL Scheme. So, as a result, I am not able to get the data on the deep links that were clicked, and that was the big problem.

In didFinishLaunchingWithOptions: launchOptions is always nil, and therefore launchOptions[.url] is nil

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool

And open url:options: does not trigger

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool

And ofcourse this one continue userActivity:restorationHandler: does not trigger

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool

So, I was only able to launch the app with no data captured. Any workaround or suggestion is appreciated. Thanks!


Solution

  • Took me sometime to resolve this issue. After trying a bunch of workaround, the solution that worked was to migrate to the new Apple's App Structure using SceneDelegate.

    To handle DeepLink from terminated state, you need to configure this function:

    var window: UIWindow?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let userActivity = connectionOptions.userActivities.first, let url = userActivity.webpageURL {            
            DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicLink, error in
                guard let dynamicLink = dynamicLink, let url = dynamicLink.url else { return }
                parseDynamicLink(fromURL: url)
            }
        }
        
        guard let windowScene = (scene as? UIWindowScene) else { return }
        
        // Initialize UIWindow
        window = UIWindow(windowScene: windowScene)
        window?.rootViewController = YOUR_ROOT_VIEW_CONTROLLER
        window?.makeKeyAndVisible()
    }
    

    To handle the other part of the scene delegate which are UIApplication equivalent:

    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        guard let url = userActivity.webpageURL else { return }
        DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicLink, error in
            guard let dynamicLink = dynamicLink, let url = dynamicLink.url else { return }
            parseDynamicLink(fromURL: url)
            
            // launch dynamic link code here...?
        }    
    }
    
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        guard let urlContext = URLContexts.first else { return }
    
        if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: urlContext.url) {
            guard let url = dynamicLink.url else { return }
            parseDynamicLink(fromURL: url)
        }    
    }
    

    If you plan to do the migration, make sure to properly configure your Info.plist's Application Scene Manifest then set your scene configuration here:

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }
    

    Your Application Scene Manifest should look something like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
        <dict>
            <key>UIApplicationSupportsMultipleScenes</key>
            <false/>
            <key>UISceneConfigurations</key>
            <dict>
                <key>UIWindowSceneSessionRoleApplication</key>
                <array>
                    <dict>
                        <key>UISceneDelegateClassName</key>
                        <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                        <key>UISceneStoryboardFile</key>
                        <string>Main</string>
                        <key>UISceneConfigurationName</key>
                        <string>Default Configuration</string>
                    </dict>
                </array>
            </dict>
        </dict>
    </plist>