16

Method scene(_ scene: UIScene, continue userActivity: NSUserActivity) doesn't get called when the app is launched after the user clicks on a universal link.

It works fine when already launched app opens again after the user clicks on the universal link. The sample code:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let incomingURL = userActivity.webpageURL,
        let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
        let path = components.path else {
            return
    }
    let params = components.queryItems ?? [URLQueryItem]()

    print("path = \(path)")
    print("params = \(params)")
}

I tried to use application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration, but it never gets called when the user clicks on the link:

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    if let scene = connectingSceneSession.scene, let userActivity = scene.userActivity {
        if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
            if let incomingURL = userActivity.webpageURL,
                let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
                let path = components.path {
                let params = components.queryItems ?? [URLQueryItem]()

                print("path = \(path)")
                print("params = \(params)")
            }
        }
    }
    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

I tried to use scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions):

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let userActivity = scene.userActivity {
        self.scene(scene, continue: userActivity)
    }
}

I also tried the following methods:

func sceneDidBecomeActive(_ scene: UIScene) {
    if let userActivity = scene.userActivity {
        self.scene(scene, continue: userActivity)
    }
}

func sceneWillEnterForeground(_ scene: UIScene) {
    if let userActivity = scene.userActivity {
        self.scene(scene, continue: userActivity)
    }
}

But scene.userActivity is always nil there and I can't get userActivity.webpageURL.

How can we recognize that the link was clicked and the app was launched (not just opened)?

Dmitry
  • 527
  • 5
  • 13
  • Related (possibly the same underlying issue): https://stackoverflow.com/questions/58134706/shortcut-item-on-app-icon-not-working-with-ios-13 – rmaddy Oct 04 '19 at 22:45

4 Answers4

15

You almost had it:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let userActivity = scene.userActivity { // <-- not quite
        self.scene(scene, continue: userActivity)
    }
}

It's not in the scene; it's in the connectionOptions . Look in the connectionOptions.userActivities. (Though if what has happened is that the user clicked a link to launch us, I would expect to find the URL in the connectionOptions.urlContexts.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 2
    still not working for me. when the app is closed. (swiped from the recent apps) the dynamic link opens the app but the url is nil – Sergio Apr 02 '20 at 11:50
  • This answer seems outdated. The other answer about iterating over connectionOptions.userActivities worked for me. – Mick Oct 22 '20 at 21:58
  • @Mick Looking at what I actually wrote, it seems to me that that's exactly what I say too: "look in the `connectionOptions.userActivities`". Iterating is how you look. I don't go into more detail because it all seems so obvious. :) – matt Oct 22 '20 at 22:04
  • @matt Sorry! I just tried out the code block without reading your notes. :) You could just remove that... – Mick Oct 22 '20 at 22:33
11

The accepted answer by Matt works for launching universal links when the app isn't already opened.

If you also want to handle universal links when the app is opened, you need both functions shown below:


// SceneDelegate.swift
// This function is called when your app launches.
// Check to see if our app was launched with a universal link. 
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
   // See if our app is being launched via universal link.
   for userActivity in connectionOptions.userActivities {
     if let universalLink = userActivity.webpageURL {
         // Do whatever you want with the universal link here.
         // NOTE: if you're navigating a web view, know that the web view will not be initialized here yet. 
         // To navigate a web view, store the URL in a variable and navigate to it once the web view is initialized.
     }
  }
}

// SceneDelegate.swift
// This function is called when your app is already running and a universal link to your app is clicked.
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    // Ensure we're trying to launch a link.
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let universalLink = userActivity.webpageURL else {
        return
    }

    // Handle the universal link here.
    // If you're navigating a web view, here's how I do it:
    //MyApp.webView.evaluateJavaScript("location.href = '\(universalLink)'")
}

I've verified this works for my app. See this Github thread for more details.

Judah Gabriel Himango
  • 58,906
  • 38
  • 158
  • 212
  • 1
    thanks this one was the answer that worked for me! – GameDev Mar 16 '22 at 12:44
  • Hi! thanks for the answer! Could you please let me know how to call the webview from viewcontroller? I dont understand MyApp.webView.evaluateJavaScript("location.href = '\(universalLink)'") – Khris Vandal Feb 09 '23 at 20:33
  • @KhrisVandal It's been awhile since I was in this code. But I think webView is just a public variable on the MyApp type. You can check out our code here: https://github.com/pwa-builder/pwabuilder-ios/tree/main/Microsoft.PWABuilder.IOS.Web/Resources/ios-project-src/pwa-shell – Judah Gabriel Himango Feb 10 '23 at 23:21
5

Apple responded confirming that issue in iOS 13.

Dmitry
  • 14,306
  • 23
  • 105
  • 189
  • 2
    Do you have a link for the issue apple confirmed? The accepted answer does not work for me, when the app is closed and I click the dynamic link, the app opens but the url is nil. – Sergio Apr 02 '20 at 13:37
  • is it fixed by now? – Jalil Oct 14 '20 at 18:35
5

This worked for me:

func scene(_ scene: UIScene, willConnectTo _: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        for userActivity in connectionOptions.userActivities {
            if let url = userActivity.webpageURL { //ADD WHATEVER CONDITION YOU NEED
                //DO WHAT YOU NEED HERE
                break
            }
        }
    }

Basically the problem is that the universal link is "hidden" inside the connectionOptions so you have to search for it with the loop.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Nicola Salvaro
  • 505
  • 1
  • 5
  • 14