continueUserActivity not called from search closed app - ios

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];
}
}

Related

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

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.

How do I check if my app is resumed by a Siri shortcut

I am working with Siri shortcuts. I wanted to know if my app is launched from a Siri shortcut in the following cases:
When the application is running in the background
When the application is not running in the background
For case 2, I can use the "didFinishLaunchingWithOptions" method but for case 1, I am not sure what approach to use.
I would appreciate any suggestions and thoughts on this topic. Thank you.
You can implement this check in your AppDelegate
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if #available(iOS 12.0, *) {
if userActivity.interaction?.intent is {YOUR_INTENT_CLASS} {
// App launched via that particular shortcut.
}
}
}

After opening iOS app "continue userActivity:" method isn't called - Firebase dynamic link

I have successfully integrated Firebase dynamic links and when I click on dynamic link then my app is opening.
The issues I'm facing is after opening app from dynamic links, continue userActivity: method should be called, but nothing happens.
I've checked the all the possible thing but didn't recognised the issue.
I've searched the SO for this but none of the answer helped me.
My Code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
GIDSignIn.sharedInstance().clientID = kGoogleSignInClientId
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
// DynamicLinks.performDiagnostics(completion: nil)
FirebaseApp.configure()
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if url.absoluteString.contains(kFBAppId) {
return FBSDKApplicationDelegate.sharedInstance().application(app, open: url, options: options)
}
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
print(dynamicLink.url ?? URL(string: "test") as Any)
return true
}
return GIDSignIn.sharedInstance().handle(url, sourceApplication: options[.sourceApplication] as? String, annotation: options[.annotation])
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
//This method is not getting called
}
Additional answer for those using separate SceneDelegate.swift apart from AppDelegate.swift:
This method in AppDelegate >
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
}
is now in SceneDelegate >
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
}
I don't know whether they keep their doc. up to date or not.
I have just copy-pasted the code from the Google's official Firebase dynamic link document.
Why was the continue userActivity: method is not called?
The reason is (See the difference in following method)
Copy pasted from google doc. - Wrong one
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
}
I wrote this (without copy paste from google doc.) - Correct one
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
}
I've made bold the difference.
This is what I was trying for many hours. It is really very frustrating for me to blindly trust on google doc.:-|
Hope this may help other.
You need to check two times.
When app. is running in the background and is opening from Link:
Delegate method is:func checkForTransferedTicket(_ userActivity: NSUserActivity) { }
When app. is NOT running in background and is opening from Link:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,options connectionOptions: UIScene.ConnectionOptions) { if let userActivity = connectionOptions.userActivities.first {
print(userActivity)
}
}

How can I get a url from a universal link?

I usually use the delegate method below when I clicking the universal link to open the app from an inactive/background state; however, it isn't called from a non-running/dead state so I can't use the NSUserActivity object, which provides me the Universal link url I need.
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
If you are aware of another solution, delegate method, or anything else that may help me get the url please let me know.
Thank you.
Through this delegate method I receive the universal link in app delegate. Please try it. It may helps you.
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
}

iOS, Universal links, Swift. continueUserActivity not calling

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
}

Resources