NSUserActivity missing userInfo on continueUserActivity

I am adding 2 crucial information NSUserActivity userInfo. But when I try to get those information on continueUserActivity, the userInfo is empty.

Here is how I am adding the info to NSUserActivity:

    self.userActivity = [[NSUserActivity alloc]
                                      initWithActivityType:@"com.mysite.example"];
    self.userActivity.title = rettt;
  
    self.userActivity.userInfo = @{@"subjectid" : subject.subjectId , @"subjecttype" : @"type"};
    NSLog(@"userInfo %@", self.recipeUserActivity.userInfo);

    self.userActivity.eligibleForSearch = YES;
    self.userActivity.eligibleForPublicIndexing = YES;
    self.userActivity.webpageURL = [NSURL URLWithString:[APIHelper websiteURL:previewUrl]];

    CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeImage];
    attributeSet.title = rettt;
    attributeSet.contentDescription = @"Description";
    attributeSet.thumbnailData = image;
    attributeSet.thumbnailURL = [NSURL URLWithString:snapshotUrl];

    self.userActivity.contentAttributeSet = attributeSet;
    [self.userActivity becomeCurrent];

Here is how I am trying to recover the information:

if ([userActivity.activityType isEqualToString:@"com.mysite.example"]){
        if ([userActivity.userInfo objectForKey:@"subjecttype"] && [userActivity.userInfo objectForKey:@"subjectid"]) {
            //Open app page
        }else {
            return false;
        }
    }else if ([[userActivity activityType] isEqualToString:CSSearchableItemActionType]) {
        if ([userActivity.userInfo objectForKey:@"kCSSearchableItemActivityIdentifier"]) {
           //open app page
        }
    }


Do you see anything wrong with this code?


Activity type of CSSearchableItemActionType works better, since I do have at least the kCSSearchableItemActivityIdentifier, but userInfo is empty regardless.


Thanks!

Accepted Reply

Thanks! What worked for me was a combination of cwagdev and John S answers. I had to add both:


When creating my NSUserActivity I had to add:


self.userActivity.userInfo = @{@"subjectId" : self.subjectId};
self.userActivity.requiredUserInfoKeys = [NSSet setWithArray:self.recipeUserActivity.userInfo.allKeys];



And I also had to overwrite updateUserActivityState on my view controller:


-(void)updateUserActivityState:(NSUserActivity *)userActivity {
    [userActivity addUserInfoEntriesFromDictionary:@{@"subjectId" : self.subjectId}];
}



Now I have all the info I need when continueUserActivity gets called.

Thanks!

Replies

Having the same issue, indexing and activityType are working fine but userinfo has 0 items on application continueUserActivity

Edit: The problem is that userActivity gets deallocated after the method you called it in, you need to create a strong referenced property so that its retained.

Did this really fix your problem? Because my userActivity has a strong referenece (otherwise the results don't even appear on the spotlight search). The problem comes later, when I click on the spotlight result and continueUserActivity is called, where (in my understanding) the object is a complete new one with an empty UserInfo, all other information, litke the title, is there.

Hey guys, I'm seeing the same issues described here:


1. You must keep a strong reference to the Activity you create, or it will not be indexed (even if you call becomeCurrent before the end of the method).

2. UserInfo is not coming through, regardless.


These both seem like bug behaviors, and if you haven't already, I encourage you to log them on bugreport.apple.com, so the engineers are made aware as well (I'm guessing they don't actively monitor all the posts here).

I was able to get mine working by filling in the requiredUserInfoKeys property, like this


self.userActivity.requiredUserInfoKeys = [NSSet setWithArray:self.userActivity.userInfo.allKeys];

That didn't do it for me, I had to implement the following.


  override func updateUserActivityState(activity: NSUserActivity) {
    activity.addUserInfoEntriesFromDictionary(["id": employee.objectId])
  }


For whatever reason when you tie your NSUserActivity instance to UIResponder.userActivity it likes to wipe out your userInfo dictionary. So I found that I either had to override this method and set the userInfo keys I am interested in each time, or don't use UIResponder's property at all and have your own strong reference, but you will need to make it current and resign current at the appropriate times (viewDidAppear/Disappear) worked for me. But I find the above approach cleaner and it doesn't feel like it's working against the system.


Hope this helps someone else...

Where are you adding this method? It appears to be a method off of UIViewController, as is the property `userActivity`. Is this how most people are doing it? I thought the recommended way was creating your own NSUserActivity and making it current.


Is that method called at the time of the NSUserActivity becoming current, or when you're relaunching the app (from activity handoff)?

I added it to my view controller, these are actually defined on `UIResponder` which `UIViewController` happens to extend from.


https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIResponder_Class/#//apple_ref/occ/instp/UIResponder/userActivity


When you use the provided `userActivity` property on UIResponder it seems to take care of the becomeCurrent/resignCurrent calls for you. It says that you're handing over control to UIKit. So far this is working fine for me, but I wonder how it works once you've got multiple UIResponders on the screen (i.e. UITextField) that can become the first responder.


There is also a very helpful note in the documentation which I missed until now that explains the behavior I was seeing.


NOTE

Prior to invoking

updateUserActivityState:
on all of the associated objects, the
userInfo
dictionary for the
NSUserActivity
object is cleared.

Is that the intended usage? As far as I understood, you're supposed to create and manage your own activity.

Things like that "everything is cleared out before calling update" are pretty troubling -- why would that be expected behavior??


If you're not using the property off view controller, using the "required keys" trick does seem to work, I was able to see my userInfo dictionary come through after specifying that.

The fact that property exists leads me to believe that is the intended purpose of it. Seems cleaner overall, albeit a little voodoo... I am sure my mind on this will flip back and forth.

Wow thanks, this totally solved the userInfo problem I was having!

Thanks! What worked for me was a combination of cwagdev and John S answers. I had to add both:


When creating my NSUserActivity I had to add:


self.userActivity.userInfo = @{@"subjectId" : self.subjectId};
self.userActivity.requiredUserInfoKeys = [NSSet setWithArray:self.recipeUserActivity.userInfo.allKeys];



And I also had to overwrite updateUserActivityState on my view controller:


-(void)updateUserActivityState:(NSUserActivity *)userActivity {
    [userActivity addUserInfoEntriesFromDictionary:@{@"subjectId" : self.subjectId}];
}



Now I have all the info I need when continueUserActivity gets called.

Thanks!

UPDATE: Behavior here is different on iOS 8 and iOS 9. The only reliable way to get this working is to use the userActivityWillSave: callback to set userInfo. It's listed here as a best practice, but it seems to be the only reliable implementation approach.


------


FWIW, I was having this issue. I was lucky that one of my Activity types worked and the other didn't:


Activity: Walking

(UserInfo x1,y1)

(UserInfo x2,y2)

(UserInfo x3,y3)

Activity: Standing

(UserInfo x4,y4)

Activity: Walking

etc.


I got userInfo if the handoff occured when standing but not walking. I got other properties such as webpageURL in all cases; just userInfo came through null.


The fix for me was to invalidate & recreate the NSUserActivity object every time (e.g. when Walking to x2/y2 from x1/y1), instead of only when Activity type changed (e.g. from walking to standing). This is very much not the way the doc is written, but fixed the issue.


Although I should be doing as recommended by Apple:

To update the activity object’s userInfo dictionary efficiently, configure its delegate and set its needsSave property to YES whenever the userInfo needs updating. At appropriate times, Handoff invokes the delegate’s userActivityWillSave: callback, and the delegate can update the activity state.


[Note: issue occurred on iOS 9 devices running code built on Xcode 6.x. Haven't tested Xcode 7 yet, and issue may not occur on iOS 8.]