I have a UITableView
(with custom cells, if that matters) hooked up to an NSFetchedResultsController
. My app also uses push notifications. When a remote notification arrives, I update my (core)data model in two phases:
1) Get title (with some custom data) and store it as a new core data Entity
. Here I call NSManagedObjectContext save()
. NSFetchedResultsController
picks up the insert and fires it's delegate method didChangeObject
with NSFetchedResultsChangeType
=NSFetchedResultsChangeInsert
. The tableview is instantly updated. (A new row is inserted)
2) Download more content related to the cell via NSURLSession
insert it into the above Entity
and again call the save() method
. NSFetchedResultsController
again picks up the update and fires it's delegate method didChangeObject
with NSFetchedResultsChangeType
=NSFetchedResultsChangeUpdate
. This is where I call my configureCell
method. The tableView is updated but with a consistent delay of about 10 seconds.
At both stages (download-context save-update tableview) the data that needs to be shown has already been persisted by core data. (For e.g. inside my configureCell..
method, I know I'm not setting any cell label to nil
or suchlike.
My NSFetchedResultsController
delegate methods:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.mainTableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.mainTableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.mainTableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:(MessageTableViewCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
}
}
Other things that I've tried inside case NSFetchedResultsChangeUpdate:
processPendingChanges
before context save
reloadRowsAtIndexPaths
instead of calling configureCell
directlybeginUpdates
and endUpdates
(even though I already have one pair which should do the job)[tableView reloadData]
numberOfRowsInSection
is not 0!NSFetchedResultsController
delegate methods run on the main thread only.In a nutshell, all (?) the proper delegate methods are being called correctly. Yet I see a consistent ~10 sec delay when updating a cell. However, insertion happens almost instantaneously. Any ideas?
@Paulw11 's comment about dispatch_async...
set me off in the right direction. The problem lies in the fact that I have been using the same ManagedObjectContext
object in two different threads even though they do not simultaneously access it at the same time. As soon as I dispatched my save
calls (which were on a background thread) on the main queue (by wrapping it inside dispatch_async(dispatch_get_main_queue,^{ UIUpdate code here });
), the delays disappeared. A possible explanation would be that calling save
on a background thread results in the delegate methods being called on the background thread.
Anyway, back to the solution - never share the same ManagedObjectContext
object between multiple threads. Use parent/child context relationships if you're updating core date in more than one thread. NSFetchedResultsController
also plays nicely with this pattern.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With