5
votes

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.

Update:

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

1 Answers

6
votes

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.

Update: More Information

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.