Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save SixBe/e2836a3cc9a415d3a5a13392856fedda to your computer and use it in GitHub Desktop.
Save SixBe/e2836a3cc9a415d3a5a13392856fedda to your computer and use it in GitHub Desktop.
extension NSFetchedResultsController {
func registerForCommitsToContext(context: NSManagedObjectContext, notificationCenter: NSNotificationCenter? = nil) -> NSObjectProtocol? {
guard self.managedObjectContext != context else {
return nil
}
let center = notificationCenter ?? NSNotificationCenter.defaultCenter()
return center.addObserverForName(NSManagedObjectContextDidSaveNotification, object: context, queue: nil) { [weak self] notification in
guard let strongSelf = self else {
return
}
strongSelf.processChangesWithRefreshObjects(notification, mergeChanges: mergeChanges)
}
}
private func insertedOrUpdatedObjectIDsMatchingFetchRequestInNotification(notification: NSNotification) -> Set<NSManagedObjectID> {
guard let entity = self.fetchRequest.entity else {
return []
}
let predicate = self.fetchRequest.predicate ?? NSPredicate(value: true)
// We are only interested in retrieving inserted and updated objects since those may be the one now matching the
// fetchRequest and which will not be handled properly if they are not already registered in out context
var matchingObjectIDs : Set<NSManagedObjectID> = []
for key in [NSUpdatedObjectsKey, NSInsertedObjectsKey] {
if let objects = notification.userInfo?[key] as? Set<NSManagedObject> {
let matching = objects.filter({$0.entity == entity && predicate.evaluateWithObject($0)}).map{$0.objectID}
matchingObjectIDs.unionInPlace(matching)
}
}
return matchingObjectIDs
}
private func processChangesWithRefreshObjects(notification: NSNotification) {
guard let _ = self.fetchRequest.predicate, _ = self.fetchRequest.entity else {
return
}
var matchingObjectIDs = self.insertedOrUpdatedObjectIDsMatchingFetchRequestInNotification(notification)
self.managedObjectContext.performBlock({
// We do not want to process objects which are already registered in our context. These objects are fine and already
// properly tracked by the default change management mechanism
let registeredObjectIDs = self.managedObjectContext.registeredObjects.map{$0.objectID}
matchingObjectIDs.subtractInPlace(registeredObjectIDs)
for matchingObjectID in matchingObjectIDs {
// Calling `existingObjectWithID` is the right thing to do here: it fetches the objects from the store
// and brings them into the main context.
// Note that it is perfectly safe, and even desirable, to get these objects here using the objectID coming from
// the background context since it will reuse the row cache and make the fetch "faster".
guard let object = try? self.managedObjectContext.existingObjectWithID(matchingObjectID) else {
continue
}
// Since we know that the objects we are processing are not part of our context and are new, calling
// this method with `mergeChanges: false` has no side effects: the object is simply faulted in memory and the
// fault will immediately fire since the object matches our fetchRequest.
self.managedObjectContext.refreshObject(object, mergeChanges: false)
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment