deep links in ios - app not in background - ios

I'm implementing deep links in ios using universal links.
From what I can see, when the app is in the background, it's working,using the sceneDelegate method
func scene(_ scene: UIScene, continue userActivity: NSUserActivity)
however, if the app is not in the background, the deep link, just opens the app without sending the information of the link.
Is it possible to redirect the user to a specific place in the app using universal links or some other way if the app is not in the background, for example after a reboot or if the user killed the app?
Thanks

That delegate method will only be triggered when the user is the app is in the background, in order to make universal links work when the app is killed you need to trigger the willConnectTo delegate method:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let userActivity = connectionOptions.userActivities.first,
userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL?.absoluteString else {
return
}
handleUniversalLink(url: url, scene: scene)
}

Related

iOS 13+: Can't get a notification userInfo when opening a deeplink

I have faced the problem that I can't solve with Notifications and Deeplinks.
When opening a deeplink UIApplication used to send a UIApplication.didFinishLaunchingNotification with userInfo that contained UIApplicationLaunchOptionsURLKey with the URL that was opened.
So we could subscribe to it like this:
NotificationCenter.default.addObserver(forName: UIApplication.didFinishLaunchingNotification, object: nil, queue: nil) { (notification) in
print(notification.userInfo) // prints UIApplicationLaunchOptionsURLKey: url_that_opened_the_app
}
Now, after iOS 13 it does not happen: userInfo is nil.
So the question is: is there a way to receive a notification from Notification Center when app opens a deeplink?
*Thoughts: *
I think that it is caused by the fact that UISceneDelegate is in charge of opening deeplinks now, which is confirmed by the fact that if we remove the SceneDelegate, we can get our userInfo back.
I tried to find if any of SceneDelegate notifications provide us with such information, but they don't: both didActivateNotification and willConnectNotification give nothing.
Yes, you can find the same with SceneDelegate as well.
Here is a Sample I made.
Once you launch your app from deep links, you will receive connectionOptions in
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
}
You can get your URL as follows:
if let context = connectionOptions.urlContexts.first as? UIOpenURLContext {
print(context.url.absoluteString)
}
So your function will look like:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
if let context = connectionOptions.urlContexts.first {
print(context.url.absoluteString)
//Here you can pass this URL to your notification and perform your logic.
}
}
DeepLink I tested was: deeplinks://mycustomDeepLink
Also I changed the scheme to Wait for the Executable to Launch to check the initial launch thing from deep links.
Note: Sometimes debugger doesn't work for whatsoever reason. So, in my sample, I added an alert view in the ViewController.

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 :)

Opening content from URL Scheme when App is closed

My issue
I'm implementing URL Schemes in my application and they're overall working fine when the app is in the foreground or the background. However, I've noticed that when it is completely closed and another app tries to access content using my URL (eg. app:page?image=1 ) which would normally work, it just opens the app but the content is never caught.
My Approach
I've set up code in both my AppDelegate and SceneDelegate methods
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:])
And
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
Desired behavior
It opens when the app is in the background, foreground or closed
Actual behavior
It only opens when in foreground or background
To handle incoming URLs we simply call this function in both the scene(_:willConnectTo:options:) and the scene(_:openURLContexts:) delegate methods:
If the App is closed:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
// Since this function isn't exclusively called to handle URLs we're not going to prematurely return if no URL is present.
if let url = connectionOptions.urlContexts.first?.url {
handleURL(url: url)
}
}
If the app is in background or foreground
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
// Get the first URL out of the URLContexts set. If it does not exist, abort handling the passed URLs and exit this method.
guard let url = URLContexts.first?.url else {
return NSLog("No URL passed to open the app")
}
handleURL(url: url)
}
You can revert to the following article for more about scene delegate and URL Schemes: Custom URL Schemes in iOS
Since your app is currently not running, it will be launched with those launch options. i.e. those options will be passed to willFinishLaunchingWithOptions: / didFinishLaunchingWithOptions: instead. Add you code to one of these methods.
For more information, read documentation about how to Respond to the Launch of Your App, or, more specifically Determine Why Your App Was Launched.
EDIT:
As commented by #paulw11 below, scene delegate works differently, and must be handled separately.
However, in Respond to Scene-Based Life-Cycle Events section, the last point is:
In addition to scene-related events, you must also respond to the
launch of your app using your UIApplicationDelegate object. For
information about what to do at app launch, see
Responding to the Launch of Your App
So I assume, we still need to handle launch in willdidFinishLaunchingWithOptions / didFinishLaunchingWithOptions.

"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?

iOS 13 performActionFor shortcutItem doesn't get called in SceneDelegate now on launch, but does after app launches. Why?

performActionFor shortcutItem gets called in my SceneDelegate if the app has already launched, but it doesn't get called if the app actually launches from a shortcut item. Why is this?
You can get the ShortcutItems from willConnectTo function in sceneDelegate when the app is launched from shortcut item (and when there is no instance of app is in background)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let shortcutItems = connectionOptions.shortcutItem{
}
}

Resources