Reputation: 6790
I would like to update a child context when it's parent context has been updated.
I have an NSFetchedResultsController using a child context that I then would like to update the UI through it's delegate - I'm not sure if this is totally a correct pattern, here's what I'm doing now:
I create a child context that gets updated from a web service in a class that supports my model. This is a simplified example:
class Messages {
var pmoc: NSManagedObjectContext!
var delegate: MessagesDelegate?
init() {
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
let moc = appDel.managedObjectContext
let pmoc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
pmoc.parentContext = moc
self.pmoc = pmoc
}
func dataToUpdatePrivateContextReceived() {
// Add things to the private moc
self.pmoc.performBlock { () -> Void in
// Create new NSManagedOBject, etc.
self.savePMOC()
}
}
func savePMOC() {
self.pmoc.performBlock { () -> Void in
do {
try self.pmoc.save()
// save main context through an abstraction...
// Inform any delegate a save has taken place
self.delegate?.pmocSavedIntoMain()
} catch let error as NSError {
print("Save pmoc error :\(error.localizedDescription)")
}
}
}
}
protocol MessagesDelegate {
func pmocSavedIntoMain()
}
Then in some UIViewController I use an NSFetchedResultsController to update a UITableView, I'm trying to use this controller with it's own private context so it's updating doesn't block the UI. Another simplified example:
class ViewController: UIViewController {
var fetchedResultsController: NSFetchedResultsController!
var viewPMOC: NSManagedObjectContext!
let messages = Messages()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
messages.delegate = self
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
let moc = appDel.managedObjectContext
let pmoc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
pmoc.parentContext = moc
self.viewPMOC = pmoc
let fr = NSFetchRequest(entityName: "MyEntity")
fr.fetchBatchSize = 20
let sort = NSSortDescriptor(key: "id", ascending: false)
fr.sortDescriptors = [sort]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fr,
managedObjectContext: self.viewPMOC,
sectionNameKeyPath: nil,
cacheName: nil)
self.fetchedResultsController.delegate = self
do {
try self.fetchedResultsController.performFetch()
} catch let error as NSError {
print("vdl fetch error is: \(error.localizedDescription)")
}
}
}
extension ViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(controller: NSFetchedResultsController) {
// dispatch begin updates on maind thread
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
// handle update type on main thread
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
// dispatch end upds on main thread
}
}
extension ViewController: MessagesDelegate {
func pmocSavedIntoMain() {
// what can I do here to get the child context to update from
//parent and thus trigger the fetched results controller to update the view?
}
}
Upvotes: 1
Views: 1214
Reputation: 80265
Fetched results controller needs a main context.
Use this pattern to get rid of the "choppiness".
RootContext (private queue) - saves to persistent store
MainContext (main queue) child of RootContext - use for UI (FRC)
WorkerContext (private queue) - child of MainContext - use for updates & inserts
The your web query is finished, create a worker context and update the data model. When you save
, the changes will be pushed up to the main context and your UI should update via the FRC delegate. Save the main and root context to persist.
Make sure you are using the block methods performBlock
and performBlockAndWait
throughout when dealing with child contexts.
Upvotes: 3
Reputation: 5188
I'm trying to use this controller with it's own private context so it's updating doesn't block the UI
The FRC's job in life is to update the UI though - this one should be on the main thread, versus as you mention, a private context who loads data. I'd put the FRC back on your main context (which will then be automatically updated when you save your child context on up), and leave it there unless you have performance problems. Then you could look at more exotic things, but FRC is aimed at keeping performance high through things such as its batchSize
.
Upvotes: 0