3

I have an NSFetchedResultsController-backed UICollectionViewController that is populated by a list of objects. Each object has a BOOL property called deleted. My fetch controller uses the following predicate to filter my objects.

[NSPredicate predicateWithFormat:@"deleted == NO"];

My issue is that when I delete the object by marking it as deleted = @YES. The subsequent didChangeObject: method tells me that the object was updated and NOT deleted. And the object does not get removed from the collection view. If I quit and reload my app, the object does not show up in the collection view, which is the correct behaviour.

Is there something that I am doing wrong, or is this the expected behaviour of NSFetchedResultsController?

Update as requested here is the code:

Fetch controller configuration:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"deleted == %@", @NO];
NSSortDescriptor *timestampSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:NO];
NSSortDescriptor *prioritySortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"priority" ascending:NO];
NSManagedObjectContext *managedObjectContext = [NSManagedObjectContext MR_defaultContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:[GYNotification description] inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
[request setIncludesSubentities:YES];
[request setSortDescriptors:sortDescriptors];       
[request setPredicate:predicate];

NSFetchedResultsController *aFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                           managedObjectContext:managedObjectContext
                                             sectionNameKeyPath:sectionNameKeyPath
                                                      cacheName:nil];

Then the fetch controller delegate methods:

- (void)controller:(NSFetchedResultsController *)controller
   didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath
     forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath {
    NSMutableDictionary *change = [NSMutableDictionary new];
    switch(type) {
        case NSFetchedResultsChangeInsert:
            change[@(type)] = newIndexPath;
            break;
        case NSFetchedResultsChangeDelete:
            change[@(type)] = indexPath;
            break;
        case NSFetchedResultsChangeUpdate:
            change[@(type)] = indexPath;
            break;
        case NSFetchedResultsChangeMove:
            change[@(type)] = @[indexPath, newIndexPath];
            break;
    }
    [_objectChanges addObject:change];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    if ([_objectChanges count] > 0) {
        [self.collectionView performBatchUpdates:^{
            for (NSDictionary *change in _objectChanges) {
                [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {
                    NSFetchedResultsChangeType type = [key unsignedIntegerValue];
                    switch (type)
                    {
                        case NSFetchedResultsChangeInsert:
                            [self.collectionView insertItemsAtIndexPaths:@[obj]];
                            break;
                        case NSFetchedResultsChangeDelete:
                            [self.collectionView deleteItemsAtIndexPaths:@[obj]];
                            break;
                        case NSFetchedResultsChangeUpdate:
                            [self.collectionView reloadItemsAtIndexPaths:@[obj]];
                            break;
                        case NSFetchedResultsChangeMove:
                            [self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];
                            break;
                    }
                }];
            }
        } completion:nil];
    }

    [_objectChanges removeAllObjects];
}
Michael Gaylord
  • 7,282
  • 8
  • 50
  • 47
  • 1
    That looks like the expected behavior since you're not actually deleting objects, but _update_ property which you created and which NSFetchedResultsController know nothing about. NSFetchedResultsController correctly detects object deletion if you do it with managedObjectContext's deleteObject: method, but how can it know about your "deleted" property? – Serhii Yakovenko Oct 08 '13 at 14:08
  • Show a bit more code for the FRC config and delegate methods. – Wain Oct 08 '13 at 14:21
  • 3
    possible duplicate of [NSFetchedResultsController updating with wrong update type](http://stackoverflow.com/questions/16283252/nsfetchedresultscontroller-updating-with-wrong-update-type) - Answer: You must not call your attribute "deleted". – Martin R Oct 08 '13 at 14:30

2 Answers2

5

That behavior sounds like what I would expect from the NSFetchedResultsController. You are really just making an update to one of your objects. The fact that it is named deleted makes no difference. In fact, calling it deleted is a little dangerous in my opinion as NSManagedObject already has an isDeleted method.

It sounds like you want to hide certain managed objects from view without really deleting them. I would suggest renaming that deleted property to something like shouldBeVisible or markedForDeletion. That way the intent is a little clearer and there won't be as much confusion as to what state your object is really in.

Jonathan Arbogast
  • 9,620
  • 4
  • 35
  • 47
1

You need to delete your managed objects like so:

NSManagedObjectContext *context = ... ;
NSManagedObject *managedObjectToDelete = ... ;

[context deleteObject:managedObjectToDelete];

Only then will your NSFetchedResultsController detect the deletion.

neural5torm
  • 773
  • 1
  • 9
  • 21