My app starts by presenting a tableview whose datasource is a Core Data SQLite store. When the app starts, a secondary thread with its own store controller and context is created to obtain updates from the web for data in the store. However, any resulting changes to the store are not notified to the fetchedresults controller (I presume because it has its own coordinator) and consequently the table is not updated with store changes. What would be the most efficient way to refresh the context on the main thread? I am considering tracking the objectIDs of any objects changed on the secondary thread, sending those to the main thread when the secondary thread completes and invoking "[context refreshObject:....] Any help would be greatly appreciated.
In your NSFetchedResultsController
handling the table, register in viewDidLoad
or loadView
for a notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:@"ContextDidSave" object:nil];
When the secondary thread is ready with the new data, simply save the context as usual, and then post the notification:
[[NSNotificationCenter defaultCenter] postNotificationName:@"ContextDidSave" object:managedObjectContext];
The notification will be handled in your NSFetchedResultsController
using the following method:
EDIT: modified the method below taking correctly into account multi-threading, after an insightful discussion with bbum.
- (void)contextDidSave:(NSNotification *)notification
{
SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
[[[[UIApplication sharedApplication] delegate] managedObjectContext] performSelectorOnMainThread:selector withObject:notification waitUntilDone:YES];
}
For UI update, it can be done automatically using the NSFetchedResultsController
delegate methods.
Finally, remember to add in the dealloc
method of the NSFetchedResultsController
the following:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Unforgiven's answer doesn't handle threading correctly. In particular, the documentation states (emphasis mine):
If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updates from your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks, such as Cocoa, generally require this behavior, but even for those that do not, keeping this behavior on the main thread has the advantage of simplifying the logic for managing your user interface.
A notification observer will be fired on whatever thread the notification was posted upon in the first place. Thus, you can't call NSTableView's reloadData
directly from the notification posted by a background thread.
There is no need to use notifications at all. In your background thread, when ready to update the user interface, use any of a number of mechanisms to reload the data in the main thread -- in the thread that manages the main event loop & user interface.
[tableView performSelectorOnMainThread: @selector(reloadData)
withObject: nil waitUntilDone: YES];
You can also use Grand Central Dispatch or NSOperation to do something similar.