The userActivity function doesn't get called in AppDelegate.swift - ios

When I tap on the result of the Core Spotlight search, the app launches, but the function in AppDelegate.swift doesn't get called:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("called")
}
which contains a logic for navigating to a specific view controller and loading data.
Some people here seem to recommend using SceneDelegate.swift:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
print("called"
}
but doesn't get called from Core Spotlight, either.
The only functions that seem to be being called are:
func sceneDidBecomeActive(_ scene: UIScene) {}
func sceneWillEnterForeground(_ scene: UIScene) {}
in SceneDelegate.swift, but doesn't have the needed userActivity parameter.

Related

Swift 5 handle Universal Links

I've implemented Universal Links in my app and it opens when specific links are clicked in emails, messages etc.
Now I'm trying to handle this events but with no success at this point.
Any ideas what am I doing wrong?
Here's a part of what I've tried so far in my AppDelegate:
import Firebase
import FirebaseMessaging
import UserNotificationsUI
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, NSUserActivityDelegate {
// ... register for notifications
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
{
print("Test 1") // Doesn't print anything
return true
}
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool
{
print("Test 2") // Doesn't print anything
return true
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
// ... handle notifications
}
P.S. I'm using Swift 5 on xCode 14
I had the same problem, the problem is that you have a SceneDelegate, because of this the AppDelegate methods are not called.
Implement the following methods in your SceneDelegate to handle Universal Links when the app is already running and when it is not launched yet:
//This method is called when app is NOT running in the background.
//Easy to test with fatalError()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
if let userActivity = connectionOptions.userActivities.first {
debugPrint("userActivity: \(userActivity.webpageURL)")
fatalError()
}
}
//This method is called when app is running in the background.
//Easy to test with debugPrint
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
debugPrint("userActivity: \(userActivity.webpageURL)")
}
From there, do whatever you need to handle the links.
Hope it helps :)

iOS Deep Link from Widget when App closed?

My widget has several links that the user can click, the link are set up as follows:
Link(destination: URL(string: "widget://start")!)
Now I am able to detect the press in the scene delegate with the following function:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let item = URLContexts.first {
UserDefaults.standard.set(item.url.absoluteString, forKey: "URL")
print(item.url)
print(URLContexts)
}
}
However, that doesn't work when the app is closed. I tried putting this block of code everywhere, scene delegate, app delegate, but I just can't find a solution on how to detect the tap when the app is closed.
Is there a way to do that?
Add this
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if let item = connectionOptions.urlContexts.first {
UserDefaults.standard.set(item.url.absoluteString, forKey: "URL")
print(item.url)
print(URLContexts)
}
}
}
For those, who use only AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let userActDic = launchOptions?[UIApplication.LaunchOptionsKey.userActivityDictionary] as? [String: Any],
let userActivity = userActDic["UIApplicationLaunchOptionsUserActivityKey"] as? NSUserActivity {
// Do with user activity
}
}
One solution is to wait a little before load url
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
...Your code
// Load the link, but set a timeout of X seconds to fix app crashing when loading deep link while app is NOT already running in the background.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.handleUniversalLink(url: url)
}
}

Universal Links callback function not called in SceneDelegate

As per my App project setup,
I have following function calls with same code to instantiate rootVCs in SceneDelegate and AppDelegate respectively
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
}

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


In order to Implement Universal Links, I have the following callback function in my App Delegate

func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
//code to capture and setup universal link
}

This function from AppDelegate is only called in less than iOS 13 devices.I looked for similar callback equivalent for SceneDelegate, The closest I could find was this function.

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
//code to capture and setup universal link
}

Configuration: Xcode Version 11.5 target iOS 10+ devices.

Problem: This particular callback is only called when there is an instance of the app running before Link is clicked. i.e. Once the App instance is killed, this function from SceneDelegate is not called and universal links Do not work for iOS13+ Devices.

I tried following this Xcode 11 - Opt out of UISceneDelegate/SwiftUI on iOS 13 to remove the Scene Delegate altogether, However ended up with only Black Screen.
Question: What am I doing wrong and what is the possible fix?


I had the same problem, the problem is that you have a SceneDelegate, because of this the AppDelegate methods are not called. So you are missing one method in your SceneDelegate, the first one you left empty will handle Universal links when the app has not been launched yet.
Implement the following methods in your SceneDelegate to handle Universal Links when the app is already running and when it is not launched yet:
//This method is called when app is NOT running in the background.
//Easy to test with fatalError()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
if let userActivity = connectionOptions.userActivities.first {
debugPrint("userActivity: \(userActivity.webpageURL)")
fatalError()
}
}
//This method is called when app is running in the background.
//Easy to test with debugPrint
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
debugPrint("userActivity: \(userActivity.webpageURL)")
}
From there, do whatever you need to handle the links.
Hope it helps :)

"openURLContexts" function not called in ios13

I have implemented custom URL schema in my project and working fine till iOS12.But not in iOS13.
For iOS13 I have added class SceneDelegate.swift and implemented below methods:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// added navigation functionality for iOS13
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
print("url:\(URLContexts.first?.url)")
}
for below ios13:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
}
Please help me to solve this issue.
Thank you.
Have you checked the LSApplicationQueriesSchemes in your Info.plist? Your custom URL schema must be mentioned there.
Maybe you can share with us your test project with this issue so we can run it?

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

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)?
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.)
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.
Apple responded confirming that issue in iOS 13.
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.

Resources