I have an intermitten issue. I delete the app from the simulator and run Xcode. The app launches for the first time. RESTKit downloads 7 objects from the server (log below). However, NSFetchedResultsController shows sections count == 0 and the tableview is blank
2014-06-06 07:14:22.541 App[4859:60b] I restkit.network:RKObjectRequestOperation.m:180 GET 'http://www.domain.com/meetings?lastrequest=2004-06-06T07%3A00%3A00Z'
2014-06-06 07:14:22.772 App[4859:7b03] I restkit.network:RKObjectRequestOperation.m:250 GET 'http://www.domain.com/meetings?lastrequest=2004-06-06T07%3A00%3A00Z' (200 OK / 7 objects) [request=0.1917s mapping=0.1528s total=0.2559s]
When I stop the app and relaunch it, I get 3 sections and 7 cells in the tableView. It's like the NSFetchedResultsController is not being updated the first time. Randomly at times the app does load the data the first time.
Below is my FRC setup:
- (NSFetchedResultsController *)fetchedResultsController
{
if(_fetchedResultsController!=nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Meetings"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *firstSort = [[NSSortDescriptor alloc] initWithKey:@"startDate"
ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:firstSort,nil];
[fetchRequest setSortDescriptors:sortDescriptors];
self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:@"sectionIdentifier"
cacheName:nil];
self.fetchedResultsController.delegate = self;
return self.fetchedResultsController;
}
FRC Delegates:
#pragma mark - NSFetchedControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView reloadData];
[self.tableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
switch (type)
{
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
{
Invite *changedInvite = [self.currentFRC objectAtIndexPath:indexPath];
STInviteSummaryCell *cell = (STInviteSummaryCell *)[self.tableView cellForRowAtIndexPath:indexPath];
[cell updateValuesForChangedInvite:changedInvite];
}
break;
case NSFetchedResultsChangeMove:
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch (type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
RESTKit Fetch:
RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc]initWithRequest:request responseDescriptors:@[responseDescriptor]];
operation.managedObjectContext = self.objectManager.managedObjectStore.mainQueueManagedObjectContext;
operation.managedObjectCache = appDelegate.managedObjectStore.managedObjectCache;
operation.savesToPersistentStore = NO;
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//Before saving the objects, here I iterate through the each object in the array and flag each attribute that's changed in the object.
NSError *error;
if ([self.managedObjectContext saveToPersistentStore:&error])
{
NSLog (@"******* OBJECTS SAVED **********");
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
[self updateUI];
});
}];
[operation start];
[operation waitUntilFinished];
}
Again the issue is intermittent.
EDIT
In the same class, I have the following code:
- (void)loadView
{
[self getManagedObjectFromAppDelegate];
[self registerForReachibilityNotificaiton];
[self fetchInvitesInBackgroundThread];
[super loadView];
}
- (void)getManagedObjectFromAppDelegate
{
STAppDelegate *appDelegate = (STAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate setupCoreDataWithRESTKit];
self.objectManager = [self getObjectManager];
self.objectManager.managedObjectStore = appDelegate.managedObjectStore;
self.objectManager.managedObjectStore.managedObjectCache = appDelegate.managedObjectStore.managedObjectCache;
self.managedObjectContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
}
It just looks like the managed object contexts aren't linked correctly, so you can use mainQueueManagedObjectContext
for everything so that they are. It's possible to create another child context to run the operation with but until profiling shows you have a performance problem you don't need to add that complexity.