NSUserActivity vs. Core Spotlight deep link - ios

I am using both NSUserActivity and Core Spotlight to index content on my app.
The reason I am using Core Spotlight is because I want to index this content before the user access (opens) it.
The problem I am facing is in the AppDelegate method:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool
Basically userActivity ONLY contains the correct information when I do this in my viewController:
userActivity = myUserActivity
But it does not work if I do:
CSSearchableIndex.default().indexSearchableItems(searchableItems).
By using the second method (Core Spotlight) I can see all my results in spotlight search, but when I tap the item, I don't have any information in the AppDelegate method.
The worst part is this:
po userActivity.activityType
"com.apple.corespotlightitem"
If I print the activityType, instead of getting my own, I get this one...
Any suggestions?

When your app is launched from a CoreSpotlight result you get a CSSearchableItemActionType.
You need to handle this type of action by retrieving the CSSearchableItemActivityIdentifier - This value will match the identifier you provided for your searchable item when you submitted it to the index.
Something like:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
guard let selectedItem = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String else {
return
}
// Do something with selectedItem
}
}

Related

iOS - AppDelegate not retrieving userActivity when Siri intent is launched

Actually making an app in swiftUI and trying to handle Siri intents to start a call in my app. I made a extension target in my project, added Siri capability and App groups in main and extension targets, my intent handler trigger the Siri request correctly at all, starting Siri and asking to make a call to some contact works perfectly, my apps gets launched and at this point is supposed to receive an activity from the intent, but nothing happen...
My intent handler looks like this:
import Intents
class IntentHandler: INExtension, INStartCallIntentHandling {
func handle(intent: INStartCallIntent, completion: #escaping (INStartCallIntentResponse) -> Void) {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INStartCallIntent.self))
guard intent.contacts?.first?.personHandle?.value != nil else {
completion(INStartCallIntentResponse(code: .failureContactNotSupportedByApp, userActivity: userActivity))
return
}
let response = INStartCallIntentResponse(code: .continueInApp, userActivity: userActivity)
completion(response)
}
func resolveContacts(for intent: INStartCallIntent, with completion: #escaping ([INStartCallContactResolutionResult]) -> Void) {
var contactName = ""
if let contacts = intent.contacts {
contactName = contacts.first?.displayName ?? ""
}
//This shared method is used to get contact data for completion
DataManager.sharedManager.findContact(contactName: contactName, with: {
contacts in
switch contacts.count {
case 1: completion([.success(with: contacts.first ?? INPerson(personHandle: INPersonHandle(value: "1800-olakase", type: .phoneNumber), nameComponents: nil, displayName: "ola k ase", image: nil, contactIdentifier: nil, customIdentifier: INPersonHandle(value: "1800-olakase", type: .phoneNumber).value))])
case 2...Int.max: completion([.disambiguation(with: contacts)])
default: completion([.unsupported()])
}
})
}
func confirm(intent: INStartCallIntent, completion: #escaping (INStartCallIntentResponse) -> Void) {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INStartCallIntent.self))
let response = INStartCallIntentResponse(code: .ready, userActivity: userActivity)
completion(response)
}
}
Added INStartCallIntent to IntentsSupported in Info.plist
So, when the code in func handle(intent: INStartCallIntent, completion: #escaping (INStartCallIntentResponse) -> Void) did complete is supposed to send the NSUserActivity to my app delegate directly to func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool ... but is not triggering that function. At the moment my app can't make new calls because is not getting any userActivity to retrieve contact data.
Also I try with func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool but is not working too.
To being more specific I follow this tutorial but changing the
INStartAudioCallIntent for INStartCallIntent because is deprecated. I don't know if swiftUI is the problem or not, I used #UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate to add my app delegate to the swiftUI life cicle
And yes, my app has request Siri authorization and enabled. Any suggestion? I'm missing something?
Ok, I figured it out: The problem was that I was trying to get the NSUserActivity from the method func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool from my AppDelegate that never gets triggered, forget about that and don't follow the documentation from apple if you are using SwiftUI because is not working anymore.
For SwiftUI you must implement .onContinueUserActivity() modifier to fetch the NSUserActivity, and from here you can do whatever you need to do. Example code:
WindowGroup {
ContentView().onContinueUserActivity(NSStringFromClass(INStartCallIntent.self), perform: { userActivity in
//do something
})
}

Navigate from userActivity handler to other view

I want to navigate to a view from a Siri suggestion. The problem is that I need the normal app flow. So first I load the normal view, which redirects and it that view I need to do an action.
But how do I know that it is called from a suggestion? I did try to set an UserDefaults key. But sometimes that property is not set (it's not set when I check the value because sync issues).
Also the notifications center is not working as I can't set the observer at the right time.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
{
if userActivity.activityType == "myId" {
UserDefaults.standard.set(true, forKey: "openViaSiri")
NotificationCenter.default.post(name: Notification.Name("openViaSiri"), object: nil)
}
return true
}
What is a good solution for this?

NSUserActivity: Which method will be called when user will tap (or will make call ) from recent log of my app?

I am implementing CallKit in swift and I'm facing a problem with NSUserActivity while making a call directly from the recent log.
While tapping on my app entry in the recent log, my app is launching but any method of AppDelegate is not calling.
I have turned on Siri from application capabilities and have added NSUserActivityType in info.plist. I've added INStartAudioCallIntent and INStartVideoCallIntent inside NSUserActivityType. The method I've implemented inside AppDelegate class is the following:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
print("Restoration")
return true
}
When tapping on any entry of my app from the recent log the following line is printed in the debugger console.
AppDelegate-BAA416CD-ED84-4161-A054-B2192AFAD47A application:continueUserActivity:restorationHandler:] has an interaction attached but it is not handled
Change the signature of the function, i.e. the restorationHandler parameter type from ([Any]?) -> Void to ([UIUserActivityRestoring]?) -> Void
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
//if its a call
guard let handle = userActivity.startCallHandle else {
print("Could not determine start call handle from user activity: \(userActivity)")
return false
}
}
For more details:
https://github.com/Lax/Learn-iOS-Swift-by-Examples/blob/master/Speakerbox/Speakerbox/AppDelegate.swift
I changed the signature and it worked for me.

How to know user is coming into app by using of Universal links

I using Universal linking in my application.
When the user pick on link directly move into root view controller.
But I want to change the rootviewcontroller when user entering through Universal link.
please help to those delegates methods.
There is 2 delegate method that will fire when application is open from link or user tap link as per below.
Swift
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// handler for URL Scheme "This will called for URL schema"
return true
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// handler for Universal Links "This will called for Universal Link"
return true
}
Use this delegate method:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool

iOS Swift 2.3: correct syntax for application restorationHandler?

I am trying to setup Firebase Dynamic Links in my iOS project using Swift 2.3.
When I add this function in the AppDelegate (as reported at the bottom of this page), I get the error:
Unknown attribute 'escaping'
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
guard let dynamicLinks = FIRDynamicLinks.dynamicLinks() else {
return false
}
let handled = dynamicLinks.handleUniversalLink(userActivity.webpageURL!) {
(dynamiclink, error) in
// ...
}
return handled
}
any idea which is the correct syntax for Swift 2.3?
The method which you are using is for swift3, here is the method for swift2.3
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
}
if you want to add restriction on deep link then implement bellow method like this
this delegate method call first.
func application(application: UIApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
return userActivityType == NSUserActivityTypeBrowsingWeb ? true : false
}
if you want to link which user have click or if you have multiple deep link in single app then want to identify then you can get like this.
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool{
// pass the url to the handle deep link call
print(userActivity.webpageURL)
//NSURLComponents
return true
}

Resources