I have a UIManagedDocument
with some data which I display in a list using NSFetchedResultsController
. The data is regularly updated in the background and changes are put onto the UIManagedDocument.managedObjectContext
(using performBlock:).
When I display the data from the main context of the document, all works as expected. But as soon as I display the list in a context which is a child of the main context (child.parentContext = document.managedObjectContext
), I don't see any objects and the following error is printed on the console:
foo[17895:15203] CoreData: error: (NSFetchedResultsController)
The fetched object at index 5 has an out of order section name 'E.
Objects must be sorted by section name'
This only happens after a new object was inserted into the documents contact. When I wait long enough for the automatic save to happen, the list displays fine. Also the problem is only when I have a sectionNameKeyPath
set on the NSFetchedResultsController
and only with the child context.
This is how I setup the fetched results controller, nothing fancy so I don't see what I could do wrong here:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Contact"];
fetchRequest.sortDescriptors = [Contact userDefinedSortDescriptors];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"hidden == nil || hidden == NO"];
NSFetchedResultsController *fetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_managedObjectContext
sectionNameKeyPath:[Contact userDefinedSectionNameKeyPath]
cacheName:@"ContactList"];
[Contact userDefinedSortDescriptors]
and [Contact userDefinedSectionNameKeyPath]
are resolved at runtime. The sort descriptors contain as first entry the sectionNameKeyPath. Neither can be nil
or other funny stuff.
Edit: Clarified some vague parts. Specifically, I don't call -save: on the documents managed object context.
Edit 2: I'll try to explain how the MOCs relate to each other.
There are three managed object contexts in play:
1) The UIManagedDocument.managedObjectContext
created by loading the document.
2) There is a background thread running which updates the objects from time to time. This is a private queue moc with the documents MOC as the parent:
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = repository.managedObjectContext;
[context performBlock:^{ /* updates */ }];
[context performBlock:^{ [context save:NULL]; }];
3) When the user wants to make changes a new MOC is created as a child of the document MOC. This is a main queue MOC. This is the context which is used to perform the fetch shown above.
The background updates are done from an NSOperationQueue but all access to the background MOC is properly surrounded with a -performBlock:
. All other accesses are done from the main thread.
Edit 3: While playing around with some settings on NSFetchRequest
I found that the problem goes away when setting fetchRequest.includesPendingChanges = NO
. But this is not a viable solution, because now the user doesn't see any updates anymore until the changes are saved in the background by UIManagedDocument.