Extract CSSearchableItem keywords

Hello,


I'm trying to get the keywords I added to an item. I run the following code and my items are indexed as expected.


A few questions to you guys:

1. How do I get the attributeSet.keywords

2. Why is it converted to userActivity: NSUserActivity in the continueUserActivity


Currently when I do:

if let keywords = userActivity.contentAttributeSet?.keywords {
                    
chartDetailTabBarController.companyName = keywords[1]
chartDetailTabBarController.searchDescription = keywords[2]
}


keywords is nil

Now I assumed since its converted to an NSUserActivity, that it might have moved to


let keywords = userActivity.keywords


but this returns keywords = []


@available(iOS 9.0, *)
func addToSpotlight(title: String, companyName: String, contentDescription: String, image: NSData?, uniqueIdentifier: String, domainIdentifier: String) {

    let attributeSet:CSSearchableItemAttributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeImage as String)
    attributeSet.title = title
    attributeSet.contentDescription = contentDescription
    attributeSet.thumbnailData = image
    attributeSet.keywords = [title, companyName, contentDescription, "Stocks", "Markets"]

    let searchableItem = CSSearchableItem(uniqueIdentifier: uniqueIdentifier, domainIdentifier: domainIdentifier, attributeSet: attributeSet)

    CSSearchableIndex.defaultSearchableIndex().indexSearchableItems([searchableItem]) { (error) -> Void in

        if let error = error {
            print("Deindexing error: \(error.localizedDescription)")
        } else {
            print("Search item successfully indexed!")
        }
    }

}


@available(iOS 9.0, *)
func createNSUserActivity(title: String, companyName: String, contentDescription: String, image: NSData?, uniqueIdentifier: String, domainIdentifier: String) {
   
    let attributeSet:CSSearchableItemAttributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeImage as String)
    attributeSet.contentDescription = contentDescription
    /
    attributeSet.relatedUniqueIdentifier = title
   
    let activity: NSUserActivity = NSUserActivity(activityType: domainIdentifier)
    activity.title = title
    activity.keywords = NSSet(array: [title, companyName, contentDescription, "Stocks", "Markets"]) as! Set<String>
    activity.userInfo = ["symbol": title, "companyName": companyName, "searchDescription": contentDescription]
    activity.contentAttributeSet = attributeSet
   
    activity.eligibleForSearch = true
    activity.eligibleForPublicIndexing = true
    activity.requiredUserInfoKeys = NSSet(array: ["title", "userInfo", "contentAttributeSet", "eligibleForSearch", "eligibleForPublicIndexing"]) as! Set<String>
   
    userActivity = activity
    activity.becomeCurrent()
   
    print("NSUserActivity created")
}

Replies

You don't get the keywords, you set the keywords. I have a suspicion that you're thinking the keywords property will give you the text that the user typed when they searched? If so, that's not how it works.


As to the NSUserActivity question, are you generating NSUserActivities for the same searchable item?

Hi


First off thanks for the reply. So if I can only set the keywords, how do I access more information that I stored in the keywords.


Regarding the NSUserActivity, I add an NSUserActivity later in the apps cycle. But even without NSUserActivity, CSSearchableItems are always handled as NSUserActivity by the continueUserActivity() method, which sort of confuses what is going on.

I think we might be mixing signals a little bit. Yes, when you index something using CoreSpotlight, it will be brought back to your app using the same infrastructure based on NSUserActivity. But you can tell the difference between an item that was handled by CoreSpotlight by checking the activityType of the NSUserActivity. If it was from CoreSpotlight then the activityType will be CSSearchableItemActionType. You can get the activity identifier (the same one you provided when creating the CSSearchableItem) by looking at the NSUserActivity.


If you're being activated because the user tapped on a search result that was created by way of an NSUserActivity then it will have all the information that you had set on it when you created the activity.


If you're doing both then it's not defined whether you'll get the CoreSpotlight version or the NSUserActivity version so you'll need to be set up to handle both.


Does that help clarify what's happening?

Yes that clarifies a little. I'm just trying to understand more.


Here is the flow:

- I added x items to CoreSpotlight by executing the function above addToSpolight. Items are indexed

- When a user does something specific I attach an NSUserActitivy by relatedUniqueIdentifier. Items are NOT duplicated and still appear


So I have a few more questions:

1. How do I extract more data from my CSSearchableItem

I want to get more than just the CSSearchableItemActivityIdentifier. I need to get at least the companyName variable

2. Why doesnt my NSUserActivity get attached

Do I need to be check in my continueUserActivity()

if activityType == CSSearchableItemActionType {
} else {
}


But In either case my if statement is met so it seems my NSUserActivity isnt being attached. (I have included that function in my initial question)

Am I correct to assume that the if statement will only go to else if I ONLY created my item as NSUserActivity ? And that if I created an CSSearchableItem WITH/WITHOUT NSUserActitivy, it will be of type CSSearchableItemActionType


Question 1 is more important to me since technically I dont want more than a CSSearchableItem as in both cases I'll be taking the user to the same spot within my app.


Thanks in advance

1. How do I extract more data from my CSSearchableItem


You cannot. If you need more data then you need to store that in your app, keyed off of the unique identifier that you use when creating the CSSearchableItem. You need to be keeping track of these unique identifiers regardless, so that when they are no longer used by the app (e.g. the user deletes whatever it's representing) you remove it from the Core Spotlight index.


2. Why doesnt my NSUserActivity get attached


I don't understand what you mean by "get attached". The NSUserActivity that comes in will have one of two activity types: CSSearchableItemActionType if it's a CoreSpotlight search result, or whatever activity type you set when you created the NSUserActivity directly based on the user's activity. In the former case all you get back is the unique identify you gave in the first place (which again, you need to be keeping track of), or you get back the NSUserActivity you created. You cannot directly control which one of the two you will get, assuming you're linking them using the relatedUniqueIdentifier.

Thanks for the feedback.


Ok let me put things in context a little:


- My app is a stocks app with 7000+ symbols

- Instead of trying to index all on launch, I'm indexing 50 at a time (As I present them to the user through a query)

- I dont have them anywhere on the web to I can NOT use webmarkup.


Approach #1: JUST CSSearchableItem (CoreSpotlight)

- I tried to index them as CSSearchableItems and that worked but I need more than the uniqueIdentifier and need them public

- They are symbols that should be ALL searchable and public


Approach #2: CSSearchableItem + NSUserActivity

- I then tried to tie an NSUserActitivy to each CSSearchableItem, hoping to make it public and add more to the userInfo dictionairy.


Approach #3: JUST NSUserActivity

- I also tried to NOT use CSSSearchable items and in my query (for loop part) I tried to add NSUserActivity items.

- That doesnt work because it seems the loop is too quick for NSUserActivities, though it works when I added them as just CSSearchableItems.


My Question #2 above referes to approach #2 here. When adding a NSUserActivity to my CSSearchable item, I'm checking if userActivity.activityType == CSSearchableItemActionType and thats being met. I thought it would be treated the activityType I gave it. So my question is, is it being attached correctly and if so, Do I now have two 2-in-1 kind of thing? So I can check if userActivity.activityType == CSSearchableItemActionType and get the CoreSpotlight and check for my activity type and get the NSUserActivity for that item?

To the extent that you have public information, making that available on a webpage somewhere would be good. You've got support and marketing pages associated with your app in iTunes Connect (all apps must provide URLs for those fields) so if these pages are crawlable off of those pages then Applebot will find them there.


I think Approach #2 is the right answer for you, but with perhaps a twist to what you're doing. You would want to index the stock symbol information using CS but then create (and associate) NSUserActivity as the user actually uses the stock information in your app. That is, NSUserActivity should be related to user activity and not as a primary vehicle for indexing content. For that reason, Approach #3 is just the wrong approach.


As I said before, unfortunately the only thing you get when a CoreSpotlight item is tapped on in the search results is the unique identifier. You need to be able to dig up your own additional information in your app when given one of those. Currently in your scenario #2, the userInfo of the NSUserActivity that you associate is not going to be automatically included on the CoreSpotlight activity if that's the one that is being handed to your app.


If you want to see something different here you need to file a bug report with details of what you would like.

so if I go with approach #2 then the userInfo of the associated NSUserActivity will not be included? What's the point of associating an NSUserActivity to CoreSpotlight? With approach #2 what will show. The CoreSpotlight item or NSUserActivity.

A key benefit of using NSUserActivity is that it can help with ranking information. When it's used to indicate what content a user is actually interacting with then that can be factored into ranking those search results higher. This is one of the key reasons that I recommend that you only use NSUserActivity to indicate what the user is actually doing. Approach #3 is attempting to shoehorn general indexing functionality which is why I don't want to see you go down that path. Approach #2 will get you the best results. Yes, it might be more work to be flexible with regard to handling either CoreSpotlight or NSUserActivity results but if you take an approach of handling the CoreSpotlight path by looking up the detail information I think you could probably then funnel both cases into a pretty common code path to handle the results regardless of where they came from.


If you associate a CSSearchableAttributeSet and set the title, contentDescription, and thumbnailData (or thumbnailURL) you can get consistent display of your results regardless of which one you use. Details in Index App Content in the App Search Programming Guide.

ok lets take a look at approach #2:


if I index using CoreSpotlight then add an NSUserAcitivty to it. Will I be able to access the userInfo of that NSUserActivity when the user selects it on search or will it be the info of CoreSpotlight?

No. The userInfo from the NSUserActivity is not magically attached to an item indexed with CoreSpotlight. Just to be clear, let's look at approach #2 in more details.


  1. You index an item with CoreSpotlight. When this item is presented in the search results and tapped on your app will receive an NSUserActivity that only contains the unique identifier for the CSSearchableItem you had provided. It's your responsibility to maintain any additional associated information if you need it. Since this can happen without #2 ever takingn place you need to be prepared to handle this scenario. It's your base case.
  2. Later the user uses your app and views/interacts with the item you indexed in #1. You generate an NSUserActivity and set the relatedUniqueIdentifier that you had used in item #1. Your app now needs to be prepared to handle this NSUserActivity that you created coming in. Note, things have to be done in this sequence. If you are setting a relatedUniqueIdentifier it must have been indexed in CoreSpotlight already. This has been discussed in other threads in this forum, I'm just pointing it out in case that's something you had missed.

Now when the user searches, Spotlight might show a result based on the NSUserActivity, or it might still show an item based on the CoreSpotlight item. This is an implementation detail. The bottom line is that it might be one or it might be the other. You need to handle both cases.

Personally since you have to handle #1 where the only thing you have to work with is the CoreSpotlight unique identifier, I would focus on that. You could simply forego trying to put any additional information on the userInfo and instead just put the relatedUniqueIdentifier. This would then let you treat both cases the same in your code. Does that make sense?

Ok I think we misunderstood approach #2, as there is definitely no magic involved


Approach #2:


1. Index the items as they are queried using CoreSpotlight

While the user is browsing whats presented to him, I

2. Create an NSUserActivity with the proper information (userInfo, relatedUniqueIdentifier and such)

3. the user then goes to search that item in search


Now that there is a CoreSpotlight and NSUserActivity for that item, and we know that when the user clicks on the search it comes back and gets treated as a NSUserActivity. So my question is, will that NSUserActivity also contain the userInfo that was saved when I created an NSUserActivity for that item? how does that scenario play out

Sorry, I'm obviously not being clear enough. So to be as succinct as possible, the answer to your question is: no. If the activity that gets handed to your app has an activityType of CSSearchableItemActionType then that is coming fromthe CoreSpotlight entry your app made. In this case the userInfo will only have the CoreSpotlight identifier in it. It will not have any userInfo entries from a NSUserActivity that your app may have also created.