i am wrting an app that uses a number of NSFetchedResultsControllers (FRCs), each for a different Managed Object subclass in my data model. several of these FRCs are part of UITableViewControllers, but several are not. i have two questions: one, why is one of my FRCs not updating when i add a new object to the MOC, and two, should i be using an FRC at all for this purpose? i thought it would save me from having to put fetch requests in all over the code, but perhaps it only makes sense to use an FRC with a tableview or other such object.
so, here are the details.
one of the FRCs not tied to a tableview is one that keeps track of schedules. the SetScheduleViewController has the following properties (among others), which are passed in via the parent view controller:
#property (weak, nonatomic) NSFetchedResultsController *scheduleFetchedResultsController;
#property (weak, nonatomic) NSManagedObjectContext *managedObjectContext;
this FRC was created by another object (which maintains a strong pointer to it) via the following code
- (NSFetchedResultsController *)scheduleFetchedResultsController {
if (_scheduleFetchedResultsController != nil) {
return _scheduleFetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Schedule" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"start" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *fRC = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
NSError *error = nil;
if (![fRC performFetch:&error]) {
IFLErrorHandler *errorHandler;
[errorHandler reportErrorToUser:error];
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
self.scheduleFetchedResultsController = fRC;
return self.scheduleFetchedResultsController;
}
(an aside: the reason the cacheName is set to nil is that i seem to be able to assign a cache name to only one of the many FRCs in this app, or the app crashes...if only one FRC is given a cache name and the rest have cache names set to nil, all seems well, though i am concerned that without a cache the performance may be terrible as the size of the persistent store grows...but that's another matter)
within the SetScheduleViewController, a schedule can be created or deleted, and on loading the SetScheduleViewController the most recently created schedule is loaded. when a schedule is created a new Schedule Managed Object is created, and then the MOC is saved as follows:
Schedule *newSchedule= [NSEntityDescription insertNewObjectForEntityForName:#"Schedule" inManagedObjectContext:self.managedObjectContext];
newSchedule.start=newStartTime;
//etc
NSError *saveError;
if (![self.managedObjectContext save:&saveError]) {
// 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 %#, %#", saveError, [saveError userInfo]);
abort();
this successfully saves the new MO, but the FRC does not update...i've checked this several ways...for example, if after saving a new schedule i re-enter the SetScheduleViewController and check [[self.scheduleFetchedResultsController fetchedObjects] count] it is not incremented. however, if i quit the app and open it again, lo and behold, the FRC fetched object count is incremented and the new object is indeed there.
i should note that the scheduleFetchedResultsController does not have a delegate assigned, unlike the FRC's attached to tableviewcontrollers, which are all working fine. i didn't assign a delegate because from what i could tell, the FRC delegate methods in the tableviewcontrollers only deal with updating the tableview when the FRC changes content...i do not see any delegate methods that refresh the FRC itself when a new object is added and the MOC saved.
so, my two questions again are: 1) why is the FRC not refreshing (and how can i make it refresh), 2) does it even make sense to use an FRC to manage fetched results for a managed object not tied to a tableview, and should i instead simply perform a fresh fetch from the MOC every time i need access to the list of objects?
any help much appreciated.
In the NSFetchedResultsController documentation it is stated that the FRC is in "no-tracking" mode if no delegate has been set. Also, the delegate must implement at least one of the change tracking delegate methods in order for change tracking to be enabled.
The delegate does not have to be a table view controller, so you could use your
SetScheduleViewController as a delegate and just implement the controllerDidChangeContent: delegate method. Inside that method, the updated
fetchedObjects is available, and you can e.g. update any UI elements in the view
controller.
Update: Passing the FRC from the parentVC does not make much sense. Each view controller should have its own FRC. So scheduleFetchedResultsController should be a method in the childVC. And as the FRC is created "lazily" in the getter method, the getter has to be called somewhere.
In the case of table view controllers, this happens because all table view data source methods
access self.fetchedResultsController.
If your childVC does not access self.fetchedResultsController then the FRC
will not be created. That could be the reason why calling [self.fetchedResultsController performFetch:&error] in viewDidLoad, as suggested in the other answer, solved your problem.
The delegate method controllerDidChangeContent: will then be called if the result
set changes during the lifetime of the childVC. That's where using an FRC makes sense.
If you just want to fetch the current set of objects when the childVC is loaded then
you don't need a FRC, and you can just execute a simple fetch, e.g. in viewDidLoad.
I've faced the same problem before. The reason is you didn't performed the fetch of FRC.
Add the following code on -viewDidLoad:
NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
Related
I am struggling to integrate UICollectionView and NSFetchedResultsControllerDelegate in this particular case.
In my view controller, I fetch a Parent entity, but display its multiple Child entities in the collection view. When creating a new Parent entity in this view controller (with insertNewObjectForEntityForName) and automatically creating a Child entity and adding it to the parent entity with addChildObject, I can add more Child entities by pressing a button, and save the object successfully. Unfortunately, for some reason the NSFetchedResultsControllerDelegate methods are not called, specifically, controllerDidChangeContent is never called and the collection view doesn't get updated.
When I fetch an already existing Parent entity, and then try to change it by adding new Child objects, the app crashes with the following exception:
*** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:],
/BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3505.17/UICollectionView.m:4211
And gives out this error in the end:
CoreData: error: Serious application error. An exception was caught
from the delegate of NSFetchedResultsController during a call to
controllerDidChangeContent:. Invalid update: invalid number of items
in section 0. The number of items contained in an existing section
after the update (5) must be equal to the number of items contained in
that section before the update (4), plus or minus the number of items
inserted or deleted from that section (1 inserted, 1 deleted) and plus
or minus the number of items moved into or out of that section (0 moved
in, 0 moved out). with userInfo (null)
What puzzles me the most, is that it shows that there is a deleted item in (1 inserted, 1 deleted), when, in fact, I am only adding an item (initializing a Child entity with insertNewObjectForEntityForName and adding it with [parent addChildObject:child].
In all these cases, I am not saving the context. I expect adding objects to the parent entity to trigger NSFetchedResultsControllerDelegate methods.
FetchResultsController setup code:
- (NSFetchedResultsController *) fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [self emojiFetchRequest];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Initializing parent object in viewWillAppear:
[[self fetchedResultsController] performFetch:&error];
id fetchedObject = [[[self fetchedResultsController] fetchedObjects] firstObject];
if (fetchedObject != nil) {
self.parent = (Parent *)fetchedObject;
self.navigationItem.title = self.parent.name;
} else {
self.navigationItem.title = #"New parent";
self.parent = [NSEntityDescription insertNewObjectForEntityForName:#"Parent" inManagedObjectContext:[self managedObjectContext]];
Child *child = [NSEntityDescription insertNewObjectForEntityForName:#"Child" inManagedObjectContext:[self managedObjectContext]];
[self.parent addFramesObject:frame];
}
Adding new child objects:
Child *child = [NSEntityDescription insertNewObjectForEntityForName:#"Child" inManagedObjectContext:[self managedObjectContext]];
[self.parent addChildrenObject:child];
[self.collectionView reloadData]; // if this isn't done, the crash happens
// but this can't animate changes
Core Data Model:
Is there a way I can see those objects? I've tried logging didChangeObject, and I only see one insert. I am not deleting anything. If anyone has any other ideas, I'd be glad to hear them.
EDIT:
If I call [self.collectionView reloadData] after adding the objects, everything seems to work correctly without crashing. But, it would be nice for the changes to be animated, which cannot be done with reloadData, and I sense there is something fundamentally wrong with what I'm doing and I would like to fix it.
Best solution: Fetch the Child entity in your fetched results controller, filter by parent.
For inserts, use the plain vanilla implementation of the NSFetchedResultsControllerDelegate. This will provide a nice animation or let you add your own.
I have two UIViewControllers in my app (relevant to this question).
Each of them has an NSFetchedResultsController with a request on the same entity called News. They both have the same sort descriptors too and they use the same UITableViewCell subclass to display everything. They also both conform to NSFetchedResultsControllerDelegate and run the delegate methods.
The only difference is that the first view controller, I will call it SummaryVC, only displays the first (up to) 6 objects fetched. Whereas the other, NewsFeedVC shows all the objects and it also pages the download of more objects. Because of this the delegate methods in SummaryVC just runs [self.tableView reloadData];
When first launching the app the SummaryVC is displayed and triggers a download of the first 6 News objects (converted from JSON) and saves them in a BG thread.
The FRC then picks up the save and displays the entities.
However...
Intermittently (I hate that word) after pushing and popping between different parts of the app. I come back to SummaryVC and the app will crash.
It is always the same crash too.
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. *** -[_PFArray objectAtIndex:]: index (46) beyond bounds (6) with userInfo (null)
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[_PFArray objectAtIndex:]: index (46) beyond bounds (6)'
In this case there were already more than 6 entities loaded in to Core Data. The 6 makes me suspicious of the FRC that belongs to SummaryVC.
I have tried several things to fix this.
Set the FRC delegate to nil on viewWillDisappear.
Set the FRC to nil on viewWillDisappear.
When the delegate methods call check that the VC is actually the VC on the screen.
In NewsFeedVC viewWillDisappear it now cancels all the download operations from its queue.
The problem seems to occur as a result of the saveContext being picked up by the FRC. i.e. if I go into the NewsFeedVC and trigger a download but then pop the VC before it finishes then that seems to trigger the crash.
Now, code wise. The crash is never with my own code.
I can show the FRC setup...
- (NSFetchedResultsController *)fetchedResultsController
{
if (!_fetchedResultsController) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"News"];
NSSortDescriptor *dateSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO];
NSSortDescriptor *sourceSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"source" ascending:YES];
NSSortDescriptor *titleSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES];
fetchRequest.sortDescriptors = #[dateSortDescriptor, sourceSortDescriptor, titleSortDescriptor];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.moc sectionNameKeyPath:nil cacheName:#"AllNewsItems"];
_fetchedResultsController.delegate = self;
NSError *error = nil;
[_fetchedResultsController performFetch:&error];
if (error) {
NSLog(#"Error performing fetch: %#, %#", error, [error userInfo]);
}
}
return _fetchedResultsController;
}
I'm thinking maybe I should pass the FRC from one object to the other. I.e. inject it into a property so that there is only one. Is that a valid thing to do?
EDIT
Could it be to do with using the same cache name for goths fetched results controllers possibly?
EDIT2
Nope, it still crashes if I change the cache name.
EDIT3
OK, I can replicate this every time. It happens if I start the NewsFeedVC scrolling and then while it is scrolling I pop back to the SummaryVC.
Thanks to Martin R for the fix.
I removed the cacheName from the initialisation of the FetchedResultsController and it fixed the problem.
Thanks.
I have a UITableViewController that's a subclass of CoreDataTableViewController (it's the Stanford class). That implements a fetchedResultsController.
Now, in my viewWillAppear, I have this:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(!self.managedObjectContext) {
[self useManagedDocument];
}
}
It initializes the managedObjectContext if I don't have one, and gets it from a helper class. In the MOC setter, I initialize the fetchedResultsController:
- (void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
{
_managedObjectContext = managedObjectContext;
if(self.managedObjectContext) {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:CD_ITEM_ENTITY_NAME];
request.sortDescriptors = #[[NSSortDescriptor
sortDescriptorWithKey:CD_ITEM_NAME_PROPERTY
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
request.predicate = nil;
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
} else {
self.fetchedResultsController = nil;
}
}
When my program starts, it loads the table data up correctly and my debugger says there was a fetch request made. However, after inserting data into my Core Data graph, and saving, it says the context changes and fires this delegate method:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
/*NSError *error;
[controller performFetch:&error];
if(error) {
NSLog(#"%#", error);
}
[self.tableView reloadData];*/
}
I commented this because it wasn't working. Basically, what I want to do is reload the data every time the context changes. If I add an item in another view controller and then go back to this one, it should reload in that case too.
How do I implement this? I tried doing performFetch in that delegate method and it entered it (I checked by setting a breakpoint inside), but the performFetch did nothing and my table wasn't reloaded.
When I add an item in a modal VC (another one I have for managing items) this is what happens in my logger:
2013-05-10 22:41:38.264 App1[7742:c07] [ItemCDTVC performFetch] fetching all Item (i.e., no predicate)
2013-05-10 22:41:46.454 App1[7742:c07] NSManagedObjects did change.
2013-05-10 22:41:46.456 App1[7742:c07] NSManagedContext did save.
When I close my app but do not quit it from the multitasking bar, and then reopen it, it does nothing. No fetch. Well, if the context didn't change I don't want it to fire a request, but imagine if the user adds an item in another ViewController and then goes back to my ItemCDTVC, which lists all items. Does it get a context changed notification so it can call the delegate method to update the table, or will I always have to refresh regardless of changes in my viewWillAppear? I currently have it set to do it only once, on app load.
Fixed, all I had to do is put a one liner in that delegate method:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
This ends updates, inserts, deletes, and changes made to the table (refreshing my view, essentially) the same as per Apple's documentation.
It now updates on content change.
I spend the whole night debugging a simple app. The app retrieves one image (yes one..intend to make my life easier) from web, and displays it in table view. I do that as a practice to learn Core Data. Before I fix it, the error message shows below:
2012-09-30 06:16:12.854 Thumbnail[34862:707] CoreData: error: Serious
application error. An exception was caught from the delegate of
NSFetchedResultsController during a call to
-controllerDidChangeContent:. Invalid update: invalid number of sections. The number of sections contained in the table view after
the update (1) must be equal to the number of sections contained in
the table view before the update (0), plus or minus the number of
sections inserted or deleted (0 inserted, 0 deleted). with userInfo
(null)
Basically it's saying that something goes wrong with FRC delegate methods. At one hand, section number changes from 0 to 1. At the other hand, "0 inserted, 0 deleted". So how the section number can increase? That should not happen.. Hence the error.
I fix the bug by simply adding [self.tableView reloadData] to my FRC setup method. I got the inspiration from this post, yet I don't quite understand it. The answer seems like too complicated and project specific. Could someone explain why adding reloadData can fix the bug? The answer might be an easy one, I hope so.
Key components of my app, if that matters:
Use UIManagedDocument to establish the core data stack
Create a helper method to download images from Flickr API
In NSManagedObject subclass file, try fetch image from persistent store. If it's not there yet, insert it into MOC.
- (void)setupFetchedResultsController
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"BigImage" inManagedObjectContext:self.document.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *imageDescriptor = [[NSSortDescriptor alloc] initWithKey:#"image" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject: imageDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20];
// Create fetch results controller
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.document.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController.delegate = self;
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Error in performFetch: %#, %#", error, [error userInfo]);
}
// Critical!! I add this line to fix the bug!
[self.tableView reloadData];
}
A fetched results controller tracks only changes to the managed object contents after the first fetch. These changes are then propagated to the table view using the delegate methods didChangeSection, didChangeObject etc.
But there is no automatic mechanism that the result of the initial fetch is sent to the table view. That is the reason why you have to call reloadData after performFetch.
However, there is a situation where this seems to work automatically. UITableViewController calls reloadData in viewWillAppear (if the table is loaded the first time). Therefore, if you set up the FRC for example in viewDidLoad, reloadData will be called in viewWillAppear, and you don't have to call it manually.
I have an NSFetchedResultsController and a few operations updates managed objects on separate threads via NSOperationQueue.
The FRC (with its predicate) looks like this:
- (NSFetchedResultsController*)fetchedResultsController
{
if(fetchedResultsController) return fetchedResultsController;
NSManagedObjectContext* mainContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Check" inManagedObjectContext:mainContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"isSync == %#", [NSNumber numberWithBool:NO]]];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
[fetchRequest release], fetchRequest = nil;
return fetchedResultsController;
}
The main thread and the threaded operation have their own managed object contexts. They only share the same coordinator.
Within the threaded operation I change the isSync property from NO to YES. To know what is Check entity to update, the main context passes to the threaded one a NSManagedObjectID.
The threaded operation retrieves the managed object like the following:
-(void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *exportContext = [[NSManagedObjectContext alloc] init];
[exportContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
//...
Check* check = (Check*)[exportContext existingObjectWithID:objID error:&error];
check.isSync = [NSNumber numberWithBool:YES];
//...
[exportContext save:&error];
[pool release], pool = nil;
}
When the thread operation calls a save the mergeChangesFromContextDidSaveNotification notification is called and the main context merges the changes.
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == [self managedObjectContext]) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
Logging the description of the notification leads to verify that changes are performed correctly.
My problem
Delegates methods of NSFetchedResultsControllerDelegate are not called.
This is quite strange since dealing with the same context, the main one, allows to listen for changes and delegates methods are called, e.g. deleting a row object in the UITableView.
I've found some topics on SO with the same problem. I've tried all the workarounds but I cannot find a valuable solution:
NSFetchedResultsController not showing updates from other contexts
NSFetchedResultsController not firing delegate method after merging update from background thread
NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext
Thank you in advance.
Edit
The code above was working in a previous model. Then I created a new model copying (and pasting) entities from the previous one and now it doesn't work anymore.
Suggestions?
Edit 2
This is the predicate I'm using in NSFetchedResultsController getter. It's my fault, but when I wrote the post I didn't copy it.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"insertionDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
// previous code here
[fetchRequest setSortDescriptors:sortDescriptors];
Now, about Jody last comment
In the main() of your NSOperation, you are loading new objects, and in
there it looks like you are setting isSync to YES for each new object.
The predicate you use for the fetchedResultsController is looking only
for objects that have isSync == NO.
I expecting that when the property isSync is set to YES, the NSFetchedResultsController observes that changes and removes rows that not match the predicate. Am I wrong?
Remember that when merging changes from the background to the main thread, I'm able to see that few objects have updated their isSync property.
You have the basic idea, so there is probably a bug somewhere in your code...
Double check that you are properly registering to receive the notification from the background MOC.
Register to receive all notifications from all objects. In that method, log the event, and all its data. When the object is a MOC, dump all its properties (especially the lists of registered, inserted, updated, and deleted objects).
Put a log statement right before and after the save call, and in the notification handler for merging the notification.
Also, you omitted a lot of code so it's hard to know what you are actually doing, but the code sample you included looks like it is hard setting isSync to YES for all objects being loaded, but your fetch request only wants those with isSync set to NO. None of those new objects will pass that predicate.
Finally, double check your model definition and make sure you are using the right number type. This can be a big source of problems.
EDIT
Oh yeah, I forgot... your fetch request does not have a sort descriptor. When you create a FRC, your fetch request must contain at least one sort descriptor... if you have multiple sections, the first sort descriptor is used to group the objects into sections.
To follow up on Alexsander's comment... I alluded to it at the beginning of my post, but you certainly do not want to listen to notifications from a MOC unless it is well known as one of yours (unless, of course, you are just logging for debugging purposes). You should know about the MOC you are using.
Furthermore, I would suggest using parent/child MOCs for this type of processing, but what you are doing should work if done properly.
Parent (private concurrency type)
Main (main concurrency type)
Then, with your background MOCs, just have them set the main moc as their parent. When they save, their objects get injected directly into the main MOC. The main MOC can then issues saves at later times to put them onto disk.
Or, you can parent you background MOC to the "parent" and then the "main" MOC can just reissue the fetch to get the data from the parent.
I just had the same problem, which I solved with parent/child contexts.
Here is the problem I had.
I was updating my Core Data object graph in a background thread which had its own managedObjectContext (which is mandatory), and my fetchedResultsController was not able to pick up the changes done to the database.
After I solved it I took some notes:
ManagedObjectContexts are not thread safe, which means that a managedObjectContext cannot be shared with other threads. If a thread needs to use a managedObjectContext, then it will initialize its own managedObjectContext.
To initialize a managedObjectContext there are two ways:
alloc/init then set its persistentStoreCoordinator property
alloc/init then set a parentContext property instead of a persistentStoreCoordinator property
note: one cannot set both the persistentStoreCoordinator and the parentContext property of a managedObjectContext.
Using parent/child contexts is needed when the a context is run on a background thread that is "linked to controllers and UI objects that are required to be used only on the main thread"(core data documentation).
Here are the requirements needed for the parent/child contexts:
the parent context lives in the main thread
the child context lives in the background thread
both contexts need to be initialized with an initWithConcurrencyType:NSMainQueueConcurrencyType method.
when the batch of changes has been done in the child context, both contexts need to perform a save operation. These save operations need to be nested in performBlock methods, i.e:
childContext performBlock:^{
[childContext save:nil];
[self.parentContext performBlock:^{
[self.parentContext save:nil];
}];
}];
EDIT: The code above is actually a bad idea for 2 reasons:
1) It works without saving the parent context.
2) The main thread is blocked if the parent context runs on it.
I hope it helped !
EDIT: Here is a stackoverflow thread which helped me greatly: Does a Core Data parent ManagedObjectContext need to share a concurrency type with the child context?