I'm currently testing universal link on my app. I have uploaded apple-app-site-association, edit my app's entitlements file. The app can be opened when I click the link(say: domain.com/test/id12345) from the Note.
However, it only opens the app, but from the app itself. the delegate method to capture the info is not be called.
the project is a SwiftUI project with iOS 13, with life cycle of UIKit App Delegate.
Under AppDelegate.swift:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("Test: this is never called:\(userActivity)")
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL,
let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return false
}
return false
}
Is anybody know how to detect the universal link clicked? How should I debug it, the link does open the app but I can't handle the rest.
If your application supports multiwindows or you didn't destroy SceneDelegate you need to implement this method in SceneDelegate
// SceneDelegate.swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
_ = LoginManager.shared.application(.shared, open: URLContexts.first?.url)
}
Related
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 :)
Any idea why starting from iOS 13 app links (universal links) this defines via Apple-App-Site-Association stopped working?
I have 2 implementations in ApplicationDelegate and in SceneDelegate.
Now works only implementation in SceneDelegate and only if application is in background, if I kill app then method continueUserActivity isn't called. I have added Haptic Feedback to track this method call but it will never be invoked neither in ActivityDelegate or SceneDelegate.
// MARK: - Universal Links support
extension SceneDelegate {
func scene(_ scene: UIScene, willContinueUserActivityWithType userActivityType: String) {
print("[Scene] Will continue user activity: ", userActivityType)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success)
}
func scene(_ scene: UIScene, didFailToContinueUserActivityWithType userActivityType: String, error: Error) {
print("[Scene] Did fail to continue user activity: ", userActivityType)
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
print("[Scene] Application continue user activity...")
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
if let url = userActivity.webpageURL {
if !present(url: url) { UIApplication.shared.open(url, options: [:]) }
}
}
}
And Application Delegate case
// MARK: - Universal Links support
extension AppDelegate {
func application(_ application: UIApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
print("[App] Will continue user activity: ", userActivityType)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.warning)
return true
}
func application(_ application: UIApplication, didFailToContinueUserActivityWithType userActivityType: String, error: Error) {
print("[App] Did fail to continue user activity: ", userActivityType)
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("[App] Application continue user activity...")
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
if let url = userActivity.webpageURL {
if !present(url: url) { UIApplication.shared.open(url, options: [:]) }
}
}
return true
}
App is being opened but the methods are not called and I cannot navigate to appropriate screen inside my app.
Ok I've found it you must do something like this
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let userActivity = connectionOptions.userActivities.first {
self.scene(scene, continue: userActivity)
}
}
For me, only solution is to delete SceneDelegate and put code inside AppDelegate and it works, whenever the app is killed or in background.
If you don use SwiftUI you can delete SceneDelegate
For the SwiftUI 2.0 users, who don't have App or SceneDelegate. My example is about Firebase universal link, which should do something once it is opened. There is a very good method called onOpenURL which you can use.
var body: some Scene {
WindowGroup {
ContentView(shouldPresentThankYouView: $shouldPresentThankYouView).onOpenURL { url in
_ = DynamicLinks.dynamicLinks().handleUniversalLink(url) { (dynamicLink, error) in
guard error == nil else{
print("Found an error! \(error!.localizedDescription)")
return
}
if let dynamicLink = dynamicLink {
shouldPresentThankYouView = true
self.handleIncomingDynamicLink(dynamicLink)
}
}
}
}
}
If the user has the app installed, once he click on the app, a Thank You View will appear. Hope it helps you when working it universal links more specifically with Firebase links.
I am trying to enable universal links on iOS, (as part of Firebase's password-less sign-up). Testing locally on iOS 13.2.
The apple-app-site-associated (AASA) JSON looks as such (https://lokitools.page.link/apple-app-site-association):
{"applinks":{"apps":[],"details":[{"appID":"43S54AHEMG.com.jkalash.Loki","paths":["NOT /_/*","/*"]}]}}
Universal links do open the app, however I am unable to handle the app opening from them. Delegate methods:
application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
do not get called, when opening from universal links. Tried both apps running in the background and force closed. AASA validator (https://branch.io/resources/aasa-validator/) says file looks good, and I have tried troubleshooting by re-installing app and observing console logs for swcd (https://ios13.dev/universal-links-debugging-on-ios-13-cjwsux93w001p6ws1swtstmzc) but nothing out of the ordinary shows up and it does look like the AASA file was downloaded.
I have also tried following Apple's troubleshooting guide (https://developer.apple.com/library/archive/qa/qa1916/_index.html) but the final step which fails (step 8) does not cover my case which is the app does open (iOS detects universal links), but the delegate methods just don't get called.
Turns out this is not a universal links specific problem, but a change in iOS 13's way of triggering app lifecycle events. Instead of coming through UIApplicationDelegate, they come through UISceneDelegate.
One confusing thing is that the app delegate methods aren't deprecated so you won't get a warning if you have both app delegate and scene delegate methods in place but only one will be called.
Refer to App delegate methods aren't being called in iOS 13 for a comprehensive answer
I am using iOS 13 with Swift 5, replace the application (: continue: restorationHandler :) method of the AppDelegate.swift file and add the scene (: continue :) method to the SceneDelgate.swift file
In my case in the SceneDelegate.swift file add the following:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let urlToOpen = userActivity.webpageURL else {
return
}
handleURL(urlToOpen)
}
Since you are able to open the app, I think all is good with your AASA file. The following delegate method gets called fine in my case:
func application(_: UIApplication, continue userActivity: NSUserActivity, restorationHandler _: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
let urlString = url.absoluteString
var queryParams: [String: String?] = [:]
if let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
let params = components.queryItems {
for param in params {
queryParams[param.name] = param.value
}
}
return true
}
Hope it helps!
If you use Google Analytics, please refer to my here. The issue may be caused by method swizzling.
Hello All – I am a newbie here ... trying to get my first iOS Swift (Xcode 11.2.1 / Swift 5 / iOS 13.2) app to open via URL and process the URL string. The app opens just fine, but the function (method?) does not appear to be getting called. Here's what I've got:
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("Here I iz ... in the URL responding bit.")
print(url)
return true
}
...
That code is in the AppDelegate.swift file in my project.
And that is what I have in my Info.plist file.
I launch via Safari both on-device and in the simulator with confirmevent://HelloWorld
As I said ... The app is opening, but I do not see any results from my print statements in the Xcode debug area.
In searching other posts they said that I need to use the "Wait for executable to be launched" to attach Xcode to the app, which I have done and it is indeed attaching. BUT I notice that none of my dozens of print statements that I have scattered about in my app appear when either when opening via "wait for executable to be launched" option.
Any/All help would be appreciated. I've spent over 5 hours scouring the web, but all indications are that is should "just work"
I just found a solution here: application(...continue userActivity...) method not called in ios 13
The trick is to also implement the SceneDelegate.swift functions for Apps with iOS > 13. This function should be called if you open the URL confirmevent://HelloWorld:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
print(URLContexts.debugDescription)
}
WindowGroup {
SampleView()
.onOpenURL { url in
//this url that was opened by your app
}
}
}
you need to use this function in your appDelegate
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let incomingURL = userActivity.webpageURL {
print(incomingURL)
}
return false
}
I working on Universal links implementation for our iOS app.
Here is my small piece of AppDelegate:
private func application(_ application: UIApplication, openURL url: URL, sourceApplication: String?, annotation: AnyObject) -> Bool {
DeepLinkHelpers.handleUniversalLink(url.absoluteString)
return true
}
private func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
DeepLinkHelpers.handleUniversalLink(userActivity.webpageURL?.absoluteString)
return true
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
signalRConnector.launch()
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.processRestartSignalRNotification(_:)), name: NSNotification.Name(rawValue: "restartSignalR"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.reachabilityChanged(_:)),
name: ReachabilityChangedNotification,
object: reachability)
do {
try reachability.startNotifier()
} catch {
Logger.save(text: "Unable to start notifier")
}
return true
}
I have processed all other steps for universal links integration:
Published apple-app-site-association file in our Web application
Switched on Associated domains feature on developers.apple.com
Specified associated domains in xcode
Checked Target Membership for entitlements file
Said xcode to wait until app will be started manually (instead of
autostart)
I doing following to debug:
Connecting ipad
Starting project in xcode
In ipad opening Calendar and click on link contained in some event.
Link has following format: app.domain.com/#/123456789
Ipad opens app but continueUserActivity not calling and i can't
handle code from the url for navigate to exact state within an app.
According documentation continueUserActivity should be executed. It is not executing both when app is running in background and when app not running.
Thank you in advance! Any help appreciated.
As of iOS 12, Swift 4.2, and Xcode 10 Apple has again changed method signature (type of restorationHandler) to
func application(_ application: UIApplication, continue userActivity:
NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
I suspect this is because you've marked the continueUserActivity function as private. I've never seen that, and in fact Swift 3 actually gives an error when I try. There don't appear to be any examples of code structured that way on all of GitHub.
I'd suggest removing the private scope.
after ios 13 you should use ScenDelegate:
//when app is running
func scene(_ scene: UIScene, continue userActivity: NSUserActivity)
//when app is not running
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
In 2019, this is what helped me (from this guide: https://medium.com/#abhimuralidharan/universal-links-in-ios-79c4ee038272 ):
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
print("Continue User Activity called: ")
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let url = userActivity.webpageURL!
print(url.absoluteString)
//handle url and open whatever page you want to open.
}
return true
}