Created
June 6, 2016 15:49
-
-
Save SixBe/e2836a3cc9a415d3a5a13392856fedda to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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