问题
Further down the rabbit hole I go. I've got my child moc saving successfully now: Core Data Parent/Child context save fail
My next hurdle is that my Main context is failing to update with the child results. Basically my tableview that had 200 entries is now blank.
Here is my setup: (contains all of the code from previous post)
In my app delegate I setup the main managedobjectcontext with the NSMainQueueConcurrencyType:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];//[[NSManagedObjectContext alloc] init];
//_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
When my data call returns the json I setup the child moc and do the work like this:
- (void)APIManager:(APIManager *)manager didGetContactsWithInfo:(NSDictionary *)info
{
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[moc setParentContext:mainMOC];
[moc setUndoManager:nil];
BCRAccount *account2 = (BCRAccount*)[moc objectWithID:[self.loggedInAccount objectID]];
[moc performBlock:^{
//do all work on child moc
//within block there is a core data relationship reference:
[contact addAccountsObject:account2];
[self saveMOC:moc completion:^(NSError *error) {
// Completion handler is called from main-thread, after save has finished
if (error) {
// Handle error
NSLog(@"save error %@", error);
} else {
NSLog(@"save success");
}
}];
}];
The saveMOC method:
- (void)saveMOC:(NSManagedObjectContext*)moc
completion:(void(^)(NSError *error))completion {
[moc performBlock:^{
NSError *error = nil;
if ([moc save:&error]) {
if (moc.parentContext) {
return [self saveMOC:moc.parentContext completion:completion];
}
}
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(error);
}
}];
}
Ok, this was saving until I re-enabled the relationship between a "Account" entity and the "Contact" entity.
The Account entity has a relationship "contacts" with a destination of "Contact" and the inverse is "accounts". The delete rule is "Nullify" and the type is "To many".
The Contact entity has a relationship "accounts" with a destination of "Account" with inverse set to "contacts". The delete rule is "Nullify" and the type is "To many".
The moc error as a result of this relationship is:
Error Domain=NSCocoaErrorDomain Code=1550 "contacts is not valid." UserInfo={Dangling reference to an invalid object.=null, NSValidationErrorValue=Relationship 'contacts' on managed object
I think this error is the source of the issue, but for context the following is the NSFetchedResultsController used in the view controller that displays the tableview of contacts:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:[BCRContact entityName] inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSArray *sortDescriptors =
@[[NSSortDescriptor sortDescriptorWithKey:@"isNewContact" ascending:NO],
[NSSortDescriptor sortDescriptorWithKey:@"status" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:@"fullName" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:@"company" ascending:YES]];
[fetchRequest setSortDescriptors:sortDescriptors];
fetchRequest.predicate = [self predicateWithSearchString:nil];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
The table generates the cells here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
ContactListCell *cell = (ContactListCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[ContactListCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
BCRContact *contact = [self.fetchedResultsController objectAtIndexPath:indexPath];
//update cell
}
Ok, That is the rough setup. The tableview would populate fine when it was just one context. So, the question is how to get the child moc data reflected properly in the tableview.
Let me know if I can provide any more details.