I'm trying to get my NSUserActivity to be indexed by the private index in Spotlight in iOS. I have followed all the steps in Apple's Index Activities and Navigation Points guide but my activity doesn't seem to be getting indexed by spotlight at all.
The guide says that:
To guarantee that the activity and its metadata get indexed, you must hold a strong reference to the activity until it gets added to the index. There are two ways to do this: The first way is to assign the activity to a property in the controller object that creates the activity. The second way is to use the userActivity property of the UIResponder object.
I opted to do the first option (to create a a property in my view controller to hold the NSUserActivity).
var lastSearchedUserActivity: NSUserActivity?
The idea is that, when the user searches for something, his last query is indexed on the device. I have the following method that prepares the user activity and (supposedly) indexes it:
func prepareLastSearchedUserActivity(tags: [String], server: Server) {
if Settings.applicationIndexedUserActivitiesAsShortcutTypes.contains(.LastSearched) {
print("Get ready to index. and tags \(tags.reduce("") { "\($0) \($1)" })")
let activity = NSUserActivity(activityType: ShortcutType.LastSearched.rawValue)
activity.title = server.serverName
activity.userInfo = ["tags": tags, "server name": server.serverName]
let attributeSet = CSSearchableItemAttributeSet()
attributeSet.contentDescription = tags.reduce("") { "\($0) \($1)" }
attributeSet.relatedUniqueIdentifier = ShortcutType.LastSearched.rawValue
activity.contentAttributeSet = attributeSet
activity.keywords = Set(tags)
activity.eligibleForSearch = true
activity.eligibleForHandoff = false
activity.becomeCurrent()
self.lastSearchedUserActivity = activity
//self.lastSearchedUserActivity?.becomeCurrent()
}
}
This method gets called without an issue, but the activity is not searchable: I have tried to search in Spotlight using the title
assigned to it, and the keywords
. The activity never shows up.
I have tried many solutions, including:
To move the eligibleForSearch
call directly after creating the activity. Apple's guide doesn't say anything about this directly, but the code snippets in the provided link seem to imply that setting this line to true
should add the activity to the index automatically.
Apple doesn't say that becomeCurrent()
should be called, but rather that it will be called for you (how? No idea). Regardless like you can see, I tried calling it myself. I also tried calling it after assigning it to my property. No dice. Apple does say that when calling becomeCurrent()
on an activity that has eligibleForSearch
as true
, it will be added to the index.
I even went as far as using the view controller's userActivity
property directly for the creation and configuration of the activity. Because I'm using the provided property, it shouldn't be deallocating early.
As far as I can tell, I am doing everything Apple does in their guide. I am completely lost.
I am testing on an iPhone 6S+ so Spotlight indexing is available. The console does not print anything related to Spotlight, either.
I just set the delegate of the activity to self
and implemented the userActivityWillSave
method.
According to the NSUserActivityDelegate
documentation, about userActivityWillSave:
Notifies the delegate that the user activity will be saved to be continued or persisted.
So this delegate method is getting called, but the indexed item is nowhere to be found. Here's the updated code:
func prepareLastSearchedUserActivity(tags: [String], server: Server) {
if Settings.applicationIndexedUserActivitiesAsShortcutTypes.contains(.LastSearched) {
print("Get ready to index. and tags \(tags.reduce("") { "\($0) \($1)" })")
let activity = NSUserActivity(activityType: ShortcutType.LastSearched.rawValue)
activity.title = server.serverName
activity.userInfo = ["tags": tags, "server name": server.serverName]
activity.delegate = self
let attributeSet = CSSearchableItemAttributeSet()
attributeSet.contentDescription = tags.reduce("") { "\($0) \($1)" }
attributeSet.relatedUniqueIdentifier = ShortcutType.LastSearched.rawValue
activity.contentAttributeSet = attributeSet
activity.keywords = Set(tags)
activity.eligibleForSearch = true
activity.eligibleForHandoff = false
self.lastSearchedUserActivity = activity
activity.becomeCurrent()
//self.lastSearchedUserActivity?.becomeCurrent()
}
}
func userActivityWillSave(userActivity: NSUserActivity) {
print("Yep it will save")
}
Turns out setting the relatedUniqueIdentifier
in the attribute set causes the userInfo
dictionary to be discarded.
I commented this line out:
attributeSet.relatedUniqueIdentifier = ShortcutType.LastSearched.rawValue
And it is now working as expected. Though I will need to find another way to delete my user activities from the index.
If you must use a unique identifier, for your activity, then you need to index it with Core Spotlight first, and then index the NSUserActivity
itself. If you try to use the relatedUniqueIdentifier
without using it in Spotlight first, the NSUserActivity
will not be indexed. This was the problem in my case.
From the relatedUniqueIdentifier
property documentation in the CSSearchableItemAttributeSet
Class Reference:
If you’re using both NSUserActivity and Core Spotlight APIs to index the same item, set this property in the activity to specify the unique identifier of the Core Spotlight item to which the activity is related, and to avoid displaying duplicate results in Spotlight.
If the unique identifier to which the activity is related hasn’t already been indexed with Core Spotlight, the activity won’t be indexed.