iOS Universal Link opens app, does not trigger app delegate methods - ios

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.

Related

IOS Universal Link can not be detected in delegate method

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

iOS Universal Linking is not Working in swift

I having the Universal linking in my application.
I place my Association file code into the server with the help of backend
Please find My URL: https://www.furnitureinfashion.net/apple-app-site-association/
Whenever I check the domain validation here: https://branch.io/resources/aasa-validator/?domain=https:%2F%2Fdevapp.meet2talk.com#resultsbox
It will show everything is fine, but when I pick on my base URL in safari it cants working.
Anyone suggest to me how do we resolve this issue.
I write the following delegate method in AppDelegate
private func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool
{
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let url = userActivity.webpageURL!
let userurl = url.absoluteString
print(userurl)
}
return true
}
Here I add the App Capabilities also.
Capabilities

URL Opening Swift App - Open Works - Called Function Does NOT work

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
}

How do I know if app was launched via Firebase-Deeplink (Dynamic Links) at AppDelegate's didFinishLaunchingWithOptions

We are adding Firebase-Deeplinks to our IOS-project, so that the app can be started via deeplink.
The deeplink-feature itself work fine so far, and so does the default app launch routine. But making both startRoutines work side by side gives me some headache.
What I am trying to achieve get's obvious looking at this code snippet.
func application(_:didFinishLaunchingWithOptions:) {
FirebaseApp.configure()
if "deeplink" {
return true
}
defaultAppLaunch() // no deeplink
return true
}
If there is a deeplink one of these appDelegate-functions is called:
func application(:continueUserActivity:restorationHandler:) {
handleDeeplink()
return true
}
func application(:openURL:options:) {
handleDeeplink()
return true
}
So how do I know at application(_:didFinishLaunchingWithOptions:) if I can call defaultAppLaunch()?
I know there is the launchOptions-Argument in but in my case it is always nil, at least when running the app via XCode. And also the Firebase-Documentation says nothing about launchOptions to be set by Firebase-Deeplinks.
Help is highly appreciated.
TL;DR
You can't know that your app was opened using deeplinks through App Delegate DidFinishLaunching.
Explaination:
App delegate did finish launch is always called, regardless if app was opened normally or via deeplinks. so you can't know through app delegate
Instead, you can know that app was opened through deeplinks if the following delegate function is called.
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
// Handle the deep link. For example, show the deep-linked content or
// apply a promotional offer to the user's account.
// ...
return true
}
return false
}
and you should handle the deeplinks functionality in the same function
I'm referencing the Firebase docs in handling dynamic links for iOS:
Firebase docs for receiving dynamic links
Next, in the application:continueUserActivity:restorationHandler:
method, handle links received as Universal Links when the app is
already installed (on iOS 9 and newer):
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
let handled = DynamicLinks.dynamicLinks().handleUniversalLink(userActivity.webpageURL!) { (dynamiclink, error) in
// ...
}
return handled
}
Finally, in the application:openURL:sourceApplication:annotation: (iOS
8 and older) and application:openURL:options: (iOS 9 and up) methods,
handle links received through your app's custom URL scheme. These
methods are called when your app receives a link on iOS 8 and older,
and when your app is opened for the first time after installation on
any version of iOS.
#available(iOS 9.0, *)
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
return application(app, open: url,
sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String,
annotation: "")
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
// Handle the deep link. For example, show the deep-linked content or
// apply a promotional offer to the user's account.
// ...
return true
}
return false
}
But you did mention that the app is currently only being run on Xcode (and I'm guessing iOS Simulator, maybe you can try it on a test device too!)

continueUserActivity not called from search closed app

I am trying to use core spotlight to open a view controller from the spotlight search results.
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray *restorableObjects))restorationHandler
{
if(self.window.rootViewController){
[self.window.rootViewController restoreUserActivityState:userActivity];
}
return YES;
}
This seems to work when the app is already running in background, however when it is closed and I tap on the spotlight search result it seems that this method gets not called and the behavior I get is that my application simply starts in the main interface.
Do you have any suggestion for making it work also when my app is closed?
Is there a way to debug what is happening (since I need to run the app to get the debugger attached I don't know how to simulate the app opening from the search result)?.
Niko,
first of all: there's a way to start your app from Xcode and not opening it immediately: open your scheme properties, go to the "run" section, and under "info", there's a switch that will help you to debug what's happening:
"Wait for executable to be launched".
If you activate this switch, you can launch the app from Xcode, Xcode will wait until the app is opened from search and then it will attach the debugger to it.
Hope that helps!
Ivan
In the new Swift 5 there is a new new file called SceneDelegate.swift.
Use the method scene(_ scene: UIScene, continue userActivity: NSUserActivity)
If you are using the Facebook SDK, and in didfinishlaunching your are returning FBSDK, instead of plain text, and returning true at the end, it can cause problems hitting continueuseractivity.
After searching a lot, and trying different ways, I just had to
return true and comment this:
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
didFinishLaunchingWithOptions needs to return YES so continueUserActivity will be called.
Add to End of application: didFinishLaunchingWithOptions:
NSDictionary *activityDictionary = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (activityDictionary) {
NSUserActivity *userActivity = activityDictionary[UIApplicationLaunchOptionsUserActivityTypeKey];
if (userActivity) {
return YES;
}
}
return NO;
continue userActivity is changed in the latest swift version. Changing the func worked for me.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool
to
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool.
If the app was closed, application: continueUserActivity: is not called. Instead, you get all the information launchOptions dictionnary in application: didFinishLaunchingWithOptions:
// In application: didFinishLaunchingWithOptions:
NSDictionary *activityDic = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (activityDic) {
// Continue activity here
}
If your app uses Scenes, make sure you have implemented both delegates:
this one for the case when the app is not running at the moment of clicking the link:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// the url is expected to be in connectionOptions.userActivities.first?.webpageURL in case if its type is NSUserActivityTypeBrowsingWeb
}
AND this one for the case when the app is in background or in foreground:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
// the url is going to be in userActivity.webpageURL
}
If your app does NOT use Scenes:
Double-check this method call carefully, because it has changed some time ago and legacy projects can still have old code:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool
This is the call which worked in my case:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
(Don't be deceived by Xcode's warning suggesting to add private - it won't help)
Check Apple's documentation about Universal Links for more details.
Here it follows the complete answer following your advices.
// In application: didFinishLaunchingWithOptions:
NSDictionary *activityDic = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (activityDic) {
if(self.window.rootViewController){
NSUserActivity * userActivity = [activityDic valueForKey:#"UIApplicationLaunchOptionsUserActivityKey"];
[self.window.rootViewController restoreUserActivityState:userActivity];
}
}

Resources