Did you reload the table view after performing the fetch again?
This worked for me:
self.fetchedResultsController = nil;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Error in refetch: %@",[error localizedDescription]);
abort();
}
[self.tableView reloadData];
UPDATE
The fetched results controller is observing Things objects for changes, NOT Domain objects. Since we are changing the Account on a Domain object, it make sense that the fetched results controller is not updating (except for the new Thing).
The ideal solution would be a way to tell the managed object context or fetched results controller which exact Things have "changed" (loop on the Things in the Domain that previously had the Account), but this would still be a manual process.
The alternative is to perform the fetch again and reload the table which worked for me.
I created this model based on your description:
Account <---> Domain <--->> Thing
To test this, I created Things "One" and "Two" in Domain domainOne and Things "Three", "Four", and "Five" in Domain domainTwo. domainOne has the Account while domainTwo has no Account.
When the app loads, the table view shows Things "One" and "Two" as expected.
When I tap "Change", domainTwo is assigned the Account and domainOne loses its account. Also, a new Thing named "Six" is created in domainTwo.
Thing "Six" appears along with "One" and "Two" even though they no longer meet the predicate. This matches the issue that you describe.
Then, I tap "Refetch" and the table reloads with "Three", "Four", "Five", and "Six". This is the desired behavior.
Here is the relevant view controller code:
- (void)addTestData {
account = [NSEntityDescription insertNewObjectForEntityForName:@"Account" inManagedObjectContext:self.managedObjectContext];
domainOne = [NSEntityDescription insertNewObjectForEntityForName:@"Domain" inManagedObjectContext:self.managedObjectContext];
domainTwo = [NSEntityDescription insertNewObjectForEntityForName:@"Domain" inManagedObjectContext:self.managedObjectContext];
[domainOne setValue:account forKey:@"domainCurrentAccount"];
NSArray *thingNames = [NSArray arrayWithObjects:@"One", @"Two", @"Three", @"Four", @"Five", nil];
for (NSString *name in thingNames) {
NSManagedObject *thing = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:self.managedObjectContext];
[thing setValue:name forKey:@"thingName"];
if (name == @"One" || name == @"Two") {
[thing setValue:domainOne forKey:@"thingDomain"];
} else {
[thing setValue:domainTwo forKey:@"thingDomain"];
}
}
}
- (void)change {
[domainTwo setValue:account forKey:@"domainCurrentAccount"];
[domainOne setValue:nil forKey:@"domainCurrentAccount"];
NSManagedObject *thing = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:self.managedObjectContext];
[thing setValue:@"Six" forKey:@"thingName"];
[thing setValue:domainTwo forKey:@"thingDomain"];
}
- (void)refetch {
self.fetchedResultsController = nil;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Error in refetch: %@",[error localizedDescription]);
abort();
}
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *changeButton = [[UIBarButtonItem alloc] initWithTitle:@"Change" style:UIBarButtonItemStyleBordered target:self action:@selector(change)];
self.navigationItem.leftBarButtonItem = changeButton;
[changeButton release];
UIBarButtonItem *refetchButton = [[UIBarButtonItem alloc] initWithTitle:@"Refetch" style:UIBarButtonItemStyleBordered target:self action:@selector(refetch)];
self.navigationItem.rightBarButtonItem = refetchButton;
[refetchButton release];
[self addTestData];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}