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?
Related
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)
}
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)
}
}
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 :)
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.
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{
}
}