Last active
January 13, 2018 12:46
-
-
Save Sorix/8201ca10d0d0340d49acdc318aafc392 to your computer and use it in GitHub Desktop.
NSFetchedResultsController for single object
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
import CoreData | |
protocol SingleFetchedResultsControllerDelegate: class { | |
func controller(didChange anObject: NSFetchRequestResult, for type: SingleFetchedResultsChangeType) | |
func controller(error: SingleFetchedResultsControllerError) | |
} | |
extension SingleFetchedResultsControllerDelegate { | |
func controller(_ controller: SingleFetchedResultsController<NSFetchRequestResult>, error: SingleFetchedResultsControllerError) { } | |
} | |
enum SingleFetchedResultsControllerError: Error { | |
case multipleResultsFetched([NSFetchRequestResult]) | |
} | |
enum SingleFetchedResultsChangeType { | |
case inserted | |
case updated | |
case deleted | |
fileprivate init?(fromNSObjectsKey key: String) { | |
switch key { | |
case NSInsertedObjectsKey: self = .inserted | |
case NSUpdatedObjectsKey: self = .updated | |
case NSDeletedObjectsKey: self = .deleted | |
default: return nil | |
} | |
} | |
fileprivate var key: String { | |
switch self { | |
case .inserted: return NSInsertedObjectsKey | |
case .updated: return NSUpdatedObjectsKey | |
case .deleted: return NSDeletedObjectsKey | |
} | |
} | |
} | |
class SingleFetchedResultsController<ResultType> where ResultType: NSFetchRequestResult { | |
let fetchRequest: NSFetchRequest<ResultType> | |
let context: NSManagedObjectContext | |
weak var delegate: SingleFetchedResultsControllerDelegate? { | |
didSet { | |
if delegate != nil { | |
addObserverIfNeeded() | |
} | |
} | |
} | |
private(set) var object: ResultType? | |
private var observerIsActive = false | |
init(managedObjectContext: NSManagedObjectContext, fetchRequest: NSFetchRequest<ResultType>) { | |
self.context = managedObjectContext | |
self.fetchRequest = fetchRequest | |
} | |
func performFetch() throws { | |
let results = try context.fetch(fetchRequest) | |
object = results.first | |
if results.count > 1 { | |
throw SingleFetchedResultsControllerError.multipleResultsFetched(results) | |
} | |
} | |
private func addObserverIfNeeded() { | |
if observerIsActive { return } | |
var observer: NSObjectProtocol? | |
observer = NotificationCenter.default.addObserver(forName: .NSManagedObjectContextDidSave, object: context, queue: .main) { [weak self] (notification) in | |
guard let me = self else { | |
if let observer = observer { | |
NotificationCenter.default.removeObserver(observer) | |
} | |
return | |
} | |
me.updateObject(from: notification, changeType: .inserted) | |
me.updateObject(from: notification, changeType: .updated) | |
me.updateObject(from: notification, changeType: .deleted) | |
} | |
observerIsActive = true | |
} | |
private func updateObject(from notification: Notification, changeType: SingleFetchedResultsChangeType) { | |
guard let modifiedObjects = notification.userInfo?[changeType.key] as? Set<NSManagedObject> else { return } | |
let objectsWithCorrectType = modifiedObjects.filter({ $0 is ResultType }) | |
let predicate = fetchRequest.predicate ?? NSPredicate(value: true) | |
let predicateFilteredObjects = NSSet(set: objectsWithCorrectType).filtered(using: predicate) | |
guard let updatedObject = predicateFilteredObjects.first as? ResultType else { return } | |
if predicateFilteredObjects.count > 1 { | |
guard let multipleObjects = Array(predicateFilteredObjects) as? [ResultType] else { | |
assertionFailure("Programmatic error") | |
return | |
} | |
delegate?.controller(error: SingleFetchedResultsControllerError.multipleResultsFetched(multipleObjects)) | |
} else { | |
self.object = updatedObject | |
delegate?.controller(didChange: updatedObject, for: changeType) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment