Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have an NSFetchedResultsController delete an object if one of the object's properties doesn't fulfil it's predicate?

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];
}
like image 373
Michael Gaylord Avatar asked Nov 23 '25 05:11

Michael Gaylord


1 Answers

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.

like image 103
Jonathan Arbogast Avatar answered Nov 25 '25 20:11

Jonathan Arbogast



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!