I am having trouble with an NSManagedObject not reflecting the changes made to a persistent store after a background thread has saved it's context.
The Setup
In a simple test application I have a single window that lists all of the objects in my core data persistent store, a search box to filter the results and a text field to show the name of the selected item and allow the name to be changed.
Bindings are as follows:
ArrayController --> AppDelegate --> ManagedObjectContext TableView Col 1 --> ArrayController --> values --> arrangedObjects.widgetName TableView Col 2 --> ArrayController --> values --> arrangedObjects.uid SearchField --> ArrayController --> predicate --> filterPredicate TextField --> ArrayController --> value --> selection.widgetName
I also have a button that starts a background (NSOperation) fetch of data from a web server.
The Process
When the user clicks the refresh button, an NSOperation is kicked off that goes and grabs the widgets asynchronously, parse the response, checks for local widgets to delete that are not in the response, new widgets to add that are not stored locally and existing local widgets that should be updated with the data retrieved form the server.
Once the processing has finished, the main context is notified using:
[mainContext performSelectorOnMainThread:
@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
I have an observer in the main controller for testing that shows the changes went through just fine and the main controller got notified.
The Problem
If I make a change to a selected object using the text field, when the data on the background thread is saved, the object in the UI is not updated to reflect those changes (i.e. it doesn't overwrite the UI with the changes from the server).
For example, given the following three widgets and ID's:
Test Name 1 | ID 123 Test Name 2 | ID 234 Test Name 3 | ID 345
If I change the name in the UI of Test Name 2
to Renamed 2
I have the following:
Test Name 1 | ID 123 Renamed 2 | ID 234 Test Name 3 | ID 345
When I refresh on the background, I want the list to reflect the server's state, ie go back to:
Test Name 1 | ID 123 Test Name 2 | ID 234 Test Name 3 | ID 345
Instead it remains:
Test Name 1 | ID 123 Renamed 2 | ID 234 Test Name 3 | ID 345
I know the persistent store was updated because if I kill the app from XCode and relaunch, the desired information is displayed. If I quit the app normally, the changed value is written to the store on application close and reopening shows the renamed value.
What I've Tried
I know the message is being sent form the background to the main context and I know the data is being persisted to the store. Therefore, the issue I believe is that the main context is not merging as I would expect it to, or I need to somehow force the array controller to fetch from the persistent store and discard it's context.
- I have tried
processPendingChanges:
upon notification of the store save, but I suspect I am just writing theRenamed 2
to the store. - I have tried doing a
rearrangeObjects
on the array controller, but as the array controller is dealing with the main context I suspect this is doing nothing - I have tried doing a
fetch:nil
on the array controller to do a fetch from the persistent store, but once again I suspect that the main context is overwriting the value ofRenamed 2
because it is not yet saved. - I have tried
fetchWithRequest:nil merge:NO error:&error
on the array controller as per the Apple docs but still this does not seem to change the displayed value
What I think needs to happen is for the array controller to save its data down to the persistent store before I write the background store data so that a fetch on the array controller will cause the data to be accurate as per my expectations. And if this is indeed the case, how would I tell the array controller to do this, or the would the array controller simply know of the changes through bindings if the main managedObjectContext was saved somehow?
I can hack the solution by doing a fetch from the persistent store, putting that data in an array and doing a setContent:
on the array controller and then repeating this when the persistent store is saved, but this feels simply wrong, not to mention the issue of then having to track the selected state of the array controller (and potentially any sub-array selections that might be happening as a result of that primary selection).
Am I off base? I'm obviously missing something here.
Any words of wisdom or advise would be very much appreciated.