29 Replies
      Latest reply on Sep 15, 2015 2:27 PM by acegreen1989
      matttancock Level 1 Level 1 (10 points)

        In my viewController, I'm creating an NSUserActivity, attaching an CSSearchableItemAttributesSet, assigning the activity to the viewController's userActivity property, and then calling becomeCurrent on the activity as below:

         

        NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:activity.uniqueIdentifier];
        CSSearchableItemAttributeSet *attribs = [self attributesForActivity:activity]; //sets title, contentDescription
        userActivity.contentAttributeSet = attribs;
        userActivity.eligibleForSearch = YES;
        userActivity.eligibleForHandoff = YES;
        userActivity.eligibleForPublicIndexing = YES;
        self.userActivity = userActivity;
        [self.userActivity becomeCurrent];
        

         

        But nothing is showing up. I've tried running this on the simulator and on an iPhone 5 device, but nothing is happening. Can't see any log files to indicate anything is amiss.

         

        If I use Core Spotlight and add the same item to the index, it shows up immediately on both device and simulator. Is the NSUserActivity integration not working yet?

        • Re: NSUserActivity results
          bbousquet Level 2 Level 2 (60 points)

          NSUserActivity does not work on the simulator (either for handoff or for search). This was confirmed to me by an engineer at WWDC 15's "Search API" lab.

          • Re: NSUserActivity results
            pdm Apple Staff Apple Staff (1,770 points)

            What build are you running?  Please file a bug report and if you can include a sample project that would be great. 

            • Re: NSUserActivity results
              mhalttu Level 1 Level 1 (0 points)

              I am having the same problem. Doesn't work on beta 4 either. Does anybody have any ideas how to debug the problem? Has somebody gotten it to work?

              • Re: NSUserActivity results
                ifbarrera Level 1 Level 1 (0 points)

                Has anyone been able to get NSUserActivity to show up on spotlight with the lastest beta? (Beta 6)

                 

                I still can't get it to work, but it's fine with CoreSpotlight API.

                  • Re: NSUserActivity results
                    cwagdev Level 1 Level 1 (20 points)

                    "still can't get it to work" meaning it's never worked for you in any of the betas? It worked fine for me in beta 4 and 5, 4 quit as the 5 released got nearer though.

                     

                    I haven't tried my project in 6 yet, actually come to think of it, there was no iOS beta 6 released. So you actually mean 5....

                     

                    Without seeing your code the only thing I can suggest is to make sure you did not set a `relatedUniqueIdentifier` if you do not have a corresponding Core Spotlight item, that prevented any of my results from appearing.

                  • Re: NSUserActivity results
                    OblivioN Level 1 Level 1 (0 points)

                    Same here, testing on Xcode Version 7.0 beta 6 (7A192o), NSUserActivity indexed item never ever shows in my search result. spent my last 4 hours on find solution, reset my simulator, and tweaking my code. nothing helps. Please advise.

                      • Re: NSUserActivity results
                        jasonjwwilliams Level 1 Level 1 (0 points)

                        NSUserActivity isn't working for me either on iOS 9.0 beta 5 on an iPad Mini 2 (Xcode 7 beta 6). I've tried all combinations of:

                        • setting/not setting the .contentAttributeSet property
                        • setting/not setting .userInfo (though I don't need it for this case) with and without .requiredUserInfoKeys
                        • also setting .needsSave = YES
                        • setting/not setting the keywords property
                        • setting/not setting the thumbnailURL (which incidentally works just fine when using CSSearchableItem).
                        • setting eligibleForHandoff = YES (and NO)

                         

                        CSSearchableItem works just fine on this device for other data we're indexing.

                         

                        Code is below:

                         

                        NSUserActivity* shortcut = [[NSUserActivity alloc] initWithActivityType:@"com.example.myapp.show_settings"];
                        shortcut.eligibleForHandoff = NO;
                        shortcut.eligibleForSearch = YES;
                        shortcut.eligibleForPublicIndexing = YES;
                        
                        
                        shortcut.title = @"Some Title";
                        
                        
                        CSSearchableItemAttributeSet* attrs = [[CSSearchableItemAttributeSet alloc]
                                                               initWithItemContentType:(__bridge NSString*)kUTTypeItem];
                        attrs.contentDescription = @"description";
                        
                        
                        shortcut.keywords = [NSSet setWithArray:@[@"keyword1",@"keyword2"]];
                        
                        
                        NSURLComponents* rsrc_path_parts = [NSURLComponents componentsWithURL:[[NSBundle mainBundle] resourceURL]
                                                                      resolvingAgainstBaseURL:YES];
                        rsrc_path_parts.path = [rsrc_path_parts.path stringByAppendingString:@"activitytype.png"];
                        attrs.thumbnailURL = rsrc_path_parts.URL;
                        shortcut.contentAttributeSet = attrs;
                        [shortcut becomeCurrent];
                        
                        
                        //Retain a reference so it makes into Spotlight
                        [_shortcut_activities addObject:shortcut];
                        

                         

                         

                        At this point I have to conclude NSUserActivity for Spotlight just doesn't work.

                          • Re: NSUserActivity results
                            mhalttu Level 1 Level 1 (0 points)

                            Same here. I feel like I've "tried everything" without success.

                            • Re: NSUserActivity results
                              mtancock Level 1 Level 1 (0 points)

                              This is all working fine for me now.

                               

                              The missing part of the puzzle was that if you set a relatedUniqueIdentifier on an NSUserActivity, it doesn't get indexed. Seems like Apple didn't envisage this usage pattern so we've been advised that if you want to do this (i.e to be able to de-duplicate between CoreSpotlight and NSUserActivity) you should create a CoreSpotlight entry first with the relevant uniqueIdentifier.

                               

                              Any updates from attributes in the NSUserActivity make it into Spotlight.

                               

                              This works fine on the simulator and devices for me, both with b5 and tested with Xcode b6.

                               

                              Other things I found important ( but it looks like you're already doing them)

                              Retaining the activity

                              Not using the userActivity property of viewController as this seems to be at the mercy of iOS so can lose its userInfo.

                               

                              It may be worth trying with a different contentType. I remember back in the early betas some content types didn't seem to get indexed.

                                • Re: NSUserActivity results
                                  pdm Apple Staff Apple Staff (1,770 points)

                                  If you are setting a relatedUniqueIdentifier on a NSUserActivity, that identifier must already have been indexed with CoreSpotlight.  Would that explain the behavior you're seeing? 

                                    • Re: NSUserActivity results
                                      jasonjwwilliams Level 1 Level 1 (0 points)

                                      Hi pdm, it doesn't. The exact code I'm using (santized parameters of course) is what I included above. These NSUserActivity instances are shortcuts into particular sections of the app so there's no need to relate them to anything else.

                                       

                                      Since they're global shortcuts, I'm creating them in didFinishLaunchingWithOptions and adding them to an NSMutableArray that's strongly held. I also tried creating them in my root view controller instead with no better luck.

                              • This reply has been hidden. This can happen if the message has been hidden by a moderator, or has been reported as abusive.
                                • Re: NSUserActivity results
                                  pdm Apple Staff Apple Staff (1,770 points)

                                  I copy/pasted the code you included into a new Single View project into the viewDidLoad method (just since it was there in the template), added a shortcut_activities property on the VC, initialized it to a mutable array (if it hadn't been initialized already), built, ran, and searched for "keyword1", "keyword2", and "some".  In all cases I got a result in Spotlight.  So in the build that I'm using it's working (granted my build is slightly newer than beta 5).  But I'm fairly confident that it'd be working in beta 5 as well.

                                   

                                  Can you check in the debugger that your array is actually non-nil?  Could you also check the value of +[CSSearchableIndex isIndexingAvailable]?  I believe iPad mini 2 is one of the supported devices, but it would be good to include that check regardless. 

                                   

                                  If you're still not able to get it to work, I'd suggest filing a bug report and attaching a sample app.

                                    • Re: NSUserActivity results
                                      jasonjwwilliams Level 1 Level 1 (0 points)

                                      Hey pdm (thanks for your help btw). I feel really dumb...we were checking isIndexing, but the NSMutableArray wasn't getting alloc/init'd before use. So the addObject had no effect of retaining a reference.

                                       

                                      Correcting that, we get what 1 out of 5 activities indexed. The code above is actually run inside a "for in" block that creates 5 "shortcut" activities and calls becomeCurrent on each one inside the loop before adding them to the array. It looks like whatever activity is in the last iteration of the loop is the one that gets created. I'm guessing that's because the becomeCurrent calls are too close together timewise?

                                        • Re: NSUserActivity results
                                          pdm Apple Staff Apple Staff (1,770 points)

                                          If I had a $1 for every time I felt dumb for the same reason.... 

                                           

                                          NSUserActivity is intended to be just that, based on actual user activity.  The fact that you're doing something in a loop indicates to me you're likely using the wrong API.  I think you should be using CoreSpotlight instead of NSUserActivity.  I would recommend switching to those APIs (which shouldn't be a large change) and see how that works out.

                                           

                                          Then as the user actually activates the activities, then generate a single NSUserActivity (using the relatedUniqueIdentitifer to link it to your previous CoreSpotlight item that you indexed).  This will give you the best results becase we'll see that you have indexed activity (CoreSpotlight) that the user has actually engaged with (NSUserActivity) and this gives us a hint that the CS items are valuable to the user, which may lead to your search results being promoted higher in search results. 

                                            • Re: NSUserActivity results
                                              OblivioN Level 1 Level 1 (0 points)

                                              Hello pdm,

                                               

                                              I have a similiar problem like jasonjwwilliams. I have a list of Restaurants, i wish to index them for my users. Actually I started with CoreSpotlight indexing because it feels like more powerful. It correctly indexed every restaurant for me and when i searched, for instance, "restarunt a", it did show me all the information about that restaurant, which is good.

                                               

                                              But the problem i had is when user clicks on the search result, indexed by "CoreSpolight", such as "restaurant a". I wish to bring them to the restaurant detail page. However, the only "userInfo" i can get from method

                                               

                                              - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler
                                              

                                               

                                              of that userActivity is the unique idenfitier, which is not enough for me to do that. So i tried to user NSUserActivity for indexing that list. Then i ended up exactly like Jason, it only indexed the last item in my array.

                                               

                                              From the WWDC Search Api video, seems like they recommending us to not using any loader and show the result directly, but it feels like CoreSpotlight indexing can't carry enough information for doing that.

                                               

                                              Please let me know if there is anything i did wrong, or I could have done. I have been struggling on this feature for a coupe of days. Please advise.

                                               

                                              Thanks.

                                                • Re: NSUserActivity results
                                                  jasonjwwilliams Level 1 Level 1 (0 points)

                                                  Hey OblivioN. Actually your use is a bit different than why we're trying to use NSUserActivity in a loop (we just want shortcuts into major areas of the app).

                                                   

                                                  For another very similar use to the one you described, we are using CSSearchableItem + CSSearchableIndex. We're using the following format for our Spotlight item IDs (since domainIdentifier on the CSSearchableItem isn't provided to willContinueActivity...which would be really helpful):

                                                   

                                                  "<type_prefix>|<item_id>"

                                                   

                                                  That lets us differentiate between different types of incoming items since they all come in with activity type CSSearchableItemActionType and no real further information. So if we were indexing recipes an item ID for us would look like:

                                                   

                                                  "recipe|b948e24a-d33f-4d39-b97b-13c08cd2f414"

                                                   

                                                  Then we look up b948e24a-d33f-4d39-b97b-13c08cd2f414 in the recipe database to pull out the rest of the information we need.

                                                   

                                                  I imagine you could use a similar delimited encoding of your own devising to put the basic info you need at retrieval time in the ID.

                                                    • Re: NSUserActivity results
                                                      OblivioN Level 1 Level 1 (0 points)

                                                      Hello Jason,

                                                       

                                                      Thank you very much for your suggestion. I still have some questions. When you mentioned "pull out from Database", you are talking about the local database in the client or your backend server? We were planning to call our backend to get all the information we need, but we saw in WWDC 2015 Search Api Seesion video, they suggested not to using any loading when showing the destination page when press search result. So that I was trying to find a way to avoid extra loading from our backend. I would except Apple provide some kinda of solution if they suggested that way.

                                                       

                                                      Don't know if you guys are using backend server for that or not, Please let me know. If there is no other way to avoid the loader on resume, i would let my team know, so we can move foward with the easier solution. Wish pdm can give a more official answers on this part.

                                                       

                                                      Thank you once again.

                                                        • Re: NSUserActivity results
                                                          jasonjwwilliams Level 1 Level 1 (0 points)

                                                          We're using a local SQLite DB for our Spotlight data, but I would do the same thing for the the data from our REST APIs we need to index.

                                                           

                                                          I watched the same video and I interpreted their comments as meaning you don't want to pop-up a "Loading ..." interstitial while you grab the content. So my approach would be to:

                                                           

                                                          1.) Store 3 or 4 of the crucial data fields in the index ID itself using a delimiter.  That would allow me to present the detail view immediately and fill it with enough info to get the user started, while I loaded the rest of the data in the background and updated the detail view as it came in.

                                                          2.) Also, when you pull the content originally from the server, I would take the result and store it in a private NSURLCache instance (you could also use a SQLite database instead) used only for caching indexed records and set them with a very long expiration stored under a special URL like recipe://uuid. THEN index it with Spotlight. On retrieval I would immediately try to fill the detail view with content from the custom NSURLCache instance (recipe://uuid), and in the background fire off a web request to the API to pull the current data...then update it when it arrives (reindex it, update the cache too).

                                                           

                                                          Combined you'd always have at least the minimum amount of data to show a user, and optimally all of the data when the cache has it, and then any staleness would be updated within a few seconds.

                                                           

                                                          -J

                                                    • Re: NSUserActivity results
                                                      jasonjwwilliams Level 1 Level 1 (0 points)

                                                      Hi pdm. Well not really...the docs on using Spotlight  all urge you to use NSUserActivity to make shortcuts into your app available.

                                                       

                                                      But there are numerous parts of the app the user won't know are there if they never use them (and hence haven't visited them yet so they would be indexed)...which obviates the recommendation to put shortcuts into Spotlight using NSUserActivity. For example, we have a Forgot Password mechanism in the app that the user may have trouble finding. It would be natural for them to type "<app> reset password" to figure out how to do it.  So we'd like to preindex that activity for them, since the reason they're searching is because they haven't been there and can't figure out how to. Does that make sense?

                                              • Re: NSUserActivity results
                                                acegreen1989 Level 1 Level 1 (0 points)

                                                So I FINALLY got it working. Here are a few comments

                                                 

                                                - setup your .plist as follows

                                                <?xml version="1.0" encoding="UTF-8"?>
                                                <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
                                                <plist version="1.0">
                                                <array>
                                                  <string>com.stockswipe.stocksSwiped</string>
                                                  <string>com.stockswipe.stocksViewed</string>
                                                </array>
                                                </plist>
                                                
                                                
                                                

                                                - setup your function as follows

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

                                                important was to create   var userActivityTest = activity which stores that activity.