NSFetchedResultsController returns incorrect NSFetchedResultsChangeType

I am creating NSFetchedResultsController

with the following Fetch request inviewDidLoad

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Person class])];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector(@selector(sortDate))
                                                               ascending:NO]];

NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                             managedObjectContext:self.mainManagedObjectContext
                                                                               sectionNameKeyPath:nil
                                                                                        cacheName:nil];
controller.delegate = delegate;
NSError *error;
if (![controller performFetch:&error]) {
    NSLogError(error);
}

// Connecting up all the delegates for UITableView

      

And reloading it in viewWillAppear

with[self.tableView reloadData]

Simplified Person

@interface Person : NSManagedObject
/// an NSTimeInterval representing the Unix Epoch
@property (nonatomic, retain) NSNumber *sortDate;
- (void)viewed;
@end

@implementation Person
// nothing fancy or custom - no extra KVO or primitive assignments
@dynamic sortDate;
- (void)viewed {
    self.sortDate = @([NSDate date].timeIntervalSince1970);
}
@end

      

My data is generated with a network request to fetch the json heap and convert it to NSManagedObject

s. The sort looks ok.

1 Person A time 1437498898
2 Person B time 1437498897
3 Person C time 1437498896

      

When I set the property sortDate

to Person C

with a method -viewed

, in <NSFetchedResultsControllerDelegate>

I get the change type NSFetchedResultsChangeMove

and the list is sorted like this:

1 Person C time 1437498900
2 Person A time 1437498898
3 Person B time 1437498897

      

When I try to do the same for Person B

, I instead get the change type NSFetchedResultsChangeUpdate

and the list is sorted like this:

1 Person C time 1437498900
2 Person A time 1437498898
3 Person B time 1437498905

      

I can constantly update sortDate

for faces A

and C

, and they will continue to sort correctly. Any changes to are Person B

not reflected until the application restarts and then Person B

stops updating. Uninstalling and reinstalling my app (clearing CoreData) doesn't help.

If I put the command print

in - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath

, then I see that it has Person B

sortDate

indeed updated to the new value, which should force NSFRC to create an event Move

instead of Update

changing the type.

// Original Sort (logs from cellForRowAtIndexPath)
Time 1437506506 - Person B
Time 1437506490 - Person C
Time 1437506381 - Person D
Time 1437506128 - Person A

// Change Person A
Updating SortDate 1437506128 -->  1437506599
Time 1437506599 - Person A for NSFetchedResultsChangeMove

// Sorted Correctly
Time 1437506599 - Person A
Time 1437506506 - Person B
Time 1437506490 - Person C
Time 1437506381 - Person D

// Change Person B
Updating SortDate 1437506506 -->  1437506610
Time 1437506610 - Person B for NSFetchedResultsChangeUpdate

// Sorted Incorrectly
Time 1437506599 - Person A
Time 1437506610 - Person B
Time 1437506490 - Person C
Time 1437506381 - Person D

      

So why is the NSFetchedResultsController

return wrong NSFetchedResultsChangeType

?

+3


source to share


2 answers


From the documentation NSFetchedResultsControllerDelegate

:

A change is reported when the changed attribute of an object is one of the sort descriptors used in the select request.

In this case, the object is supposed to be updated, but no delegate is sent to a separate update message.

An update is reported when the state of the objects changes, but the changed attributes are not part of the sort keys.

Hopefully this makes it clear why you are getting different types of changes in your callback. With this in mind, I would suggest clearing the selected result controller and delegating the callbacks:

  • Eliminate redundant predicate in FRC
  • Use the sort key is the name of the attribute: "sortDate"

    .
  • Make sure the delegate callback method moves the table view cell to the new pointer path i.e. by calling deleteRowsAtIndexPaths

    and insertRowsAtIndexPaths

    as needed.


Final thought: The erroneous behavior you describe for one particular object (while others seem to work as expected) indicates other possible errors, for example in relation to attribute updates sortDate

.

Also, you didn't explain the variable keypath

in the selected result controller. The grouping may be contributing to the behavior that you observe.

Finally, it is possible that you are a victim of the rare target dispatcher delegate bug described here (including the workaround). Also, don't forget that if you don't need to animate the changes, you can always just invalidate the FRC and call reloadData

.

If all else fails, I would refactor with NSDate

instead NSNumber

, which is more intuitive anyway.

+5


source


@Mundy - I'm stuck with a similar issue. With the workaround Apple suggested here , do you know if the newIndexPath / indexPath will have an updated indexPath that needs to be part of the NSFetchedResultsChangeMove?



0


source







All Articles