Reputation: 6856
I'm creating an NSFetchedResultsController
with the following Fetch Request in viewDidLoad
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]
A 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 a bunch of json and convert it into NSManagedObject
s. The sorting looks fine.
1 Person A time 1437498898
2 Person B time 1437498897
3 Person C time 1437498896
When I adjust the sortDate
property on Person C
with the -viewed
method, in the <NSFetchedResultsControllerDelegate>
I get a change type of 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 attempt to do the same for Person B
I instead get a change type of 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 continuously update the sortDate
for Persons A
and C
and they'll continue to sort correctly. Any changes to Person B
don't show until after the app is rebooted but then Person B
stops updating. Deleting and reinstalling my app (to clear CoreData) doesn't help.
If I place a print
command in the - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
then I can see that Person B
's sortDate
has indeed updated to the new value, which should cause the NSFRC to create a Move
event instead of Update
change 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
returning the wrong NSFetchedResultsChangeType
?
Upvotes: 3
Views: 498
Reputation: 80265
From the documentation of NSFetchedResultsControllerDelegate
:
A move is reported when the changed attribute on the object is one of the sort descriptors used in the fetch request.
An update of the object is assumed in this case, but no separate update message is sent to the delegate.
An update is reported when an object’s state changes, but the changed attributes aren’t part of the sort keys.
I hope this makes it clear why you get different change types in your callback. Considering this, I would advise to clean up your fetched results controller and delegate callbacks:
"sortDate"
.deleteRowsAtIndexPaths
and insertRowsAtIndexPaths
as appropriate.Final thought: the erratic behavior you describe for one particular objects (while the others seem to work as expected) points to other possible errors, e.g in connection with updating the sortDate
attribute.
Also, you did not explain the keypath
variable in your fetched results controller. Maybe the grouping is contributing to the behavior you are observing.
Finally, it is possible that you are the victim of a rare malfunction of the fetched results controller delegate described here (including workaround). - Also, don't forget that if you do not need to animate the changes, you can always just invalidate the FRC and call reloadData
.
If nothing helps I would refactor with NSDate
instead of NSNumber
which is anyway more intuitive.
Upvotes: 5