I Änderungen von besonderer NSManagedObject
beobachten wollen und dementsprechend UI aktualisieren.Coredata: Mich benachrichtigen wenn NSManagedObject ohne Bezug auf NSManagedObject zu halten geändert wird
I wollen nicht zu behalten Bezug auf NSManagedObject
, weil es jederzeit entfernt werden kann (d. H. Durch das Ergebnis der Remote-Push-Benachrichtigung).
Im Moment richte ich NSFetchRequest
, NSFetchedResultsController
und NSFetchedResultsControllerDelegate
ein, um dies zu erreichen. Aber ich möchte diese Lösung vereinfachen (siehe unten).
Gibt es eine einfache Möglichkeit, Änderungen in ohne Verwendung von NSFetchedResultsControllerDelegate
zu beobachten?
Vielen Dank!
Beispielcode (Xcode Spielplatz)
import PlaygroundSupport
import Cocoa
import CoreData
PlaygroundPage.current.needsIndefiniteExecution = true
extension NSManagedObject {
public static var entityName: String {
let className = String(describing: self)
return className.components(separatedBy: ".").last!
}
public convenience init(in context: NSManagedObjectContext) throws {
let entityName = type(of: self).entityName
guard let entityDescription = NSEntityDescription.entity(forEntityName: entityName, in: context) else {
fatalError()
}
self.init(entity: entityDescription, insertInto: context)
}
}
@objc(UserInfoEntity)
class UserInfoEntity: NSManagedObject {
@NSManaged var id: Int64
@NSManaged var name: String
convenience init(id: Int64, name: String, in context: NSManagedObjectContext) throws {
try self.init(in: context)
self.id = id
self.name = name
}
}
class DBStack {
static let shared = DBStack()
static var mainContext: NSManagedObjectContext {
return shared.mainContext
}
private typealias PSC = NSPersistentStoreCoordinator
private lazy var coordinator: PSC = PSC(managedObjectModel: self.model)
private lazy var model: NSManagedObjectModel = self.setupModel()
private lazy var writerContext: NSManagedObjectContext = self.setupWriterContext()
private lazy var mainContext: NSManagedObjectContext = self.setupMainContext()
private var isInitialized = false
init() {
}
func setupInMemoryStore() throws {
guard !isInitialized else { return }
isInitialized = true
try coordinator.addPersistentStore(ofType: NSInMemoryStoreType,
configurationName: nil, at: nil, options: nil)
}
static func makeChildContext() -> NSManagedObjectContext {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.parent = mainContext
return moc
}
private func setupWriterContext() -> NSManagedObjectContext {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}
private func setupMainContext() -> NSManagedObjectContext {
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.parent = writerContext
return moc
}
private func setupModel() -> NSManagedObjectModel {
let attributeID = NSAttributeDescription()
attributeID.name = "id"
attributeID.attributeType = .integer64AttributeType
attributeID.isOptional = false
attributeID.isIndexed = true
let attributeName = NSAttributeDescription()
attributeName.name = "name"
attributeName.attributeType = .stringAttributeType
attributeName.isOptional = false
let entityUserInfo = NSEntityDescription()
entityUserInfo.name = "UserInfoEntity"
entityUserInfo.managedObjectClassName = "UserInfoEntity"
entityUserInfo.properties = [attributeID, attributeName]
let model = NSManagedObjectModel()
model.entities = [entityUserInfo]
return model
}
}
class FetchedResultsDelegate: NSObject, NSFetchedResultsControllerDelegate {
public var entityChanged: ((Void) -> Void)?
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
entityChanged?() // Notify about content change.
}
}
// Create or Update user info.
func updateUserInfo(id: Int64, name: String) {
let privateContext = DBStack.makeChildContext()
privateContext.perform {
let request: NSFetchRequest<UserInfoEntity> = NSFetchRequest(entityName: UserInfoEntity.entityName)
request.predicate = NSPredicate(format: "%K == %@", argumentArray: [#keyPath(UserInfoEntity.id), id])
request.fetchLimit = 1
do {
if let userInfo = try privateContext.fetch(request).first {
userInfo.name = name
} else {
_ = try UserInfoEntity(id: id, name: name, in: privateContext)
}
if privateContext.hasChanges {
print("→ Will save userInfo. Name: " + name)
try privateContext.save()
}
} catch {
print(error)
}
}
}
let stack = DBStack()
try stack.setupInMemoryStore()
let userID: Int64 = 1
let request: NSFetchRequest<UserInfoEntity> = NSFetchRequest(entityName: UserInfoEntity.entityName)
request.predicate = NSPredicate(format: "%K == %@", argumentArray: [#keyPath(UserInfoEntity.id), userID])
request.sortDescriptors = [NSSortDescriptor(key: #keyPath(UserInfoEntity.name), ascending: false)]
let delegate = FetchedResultsDelegate()
let fetchedResultsController: NSFetchedResultsController<UserInfoEntity>
= NSFetchedResultsController(fetchRequest: request, managedObjectContext: DBStack.mainContext,
sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = delegate
// Here is our event handler. Called on main thread.
delegate.entityChanged = { [weak fetchedResultsController] in
let userInfo = fetchedResultsController?.fetchedObjects?.first
print("! UserInfo changed: \(String(describing: userInfo?.name))")
// Update UI.
}
try fetchedResultsController.performFetch()
DispatchQueue.global().async {
updateUserInfo(id: userID, name: "Alex")
updateUserInfo(id: userID, name: "Alexander")
}
Wird drucken:
→ Will save userInfo. Name: Alex
! UserInfo changed: Optional("Alex")
→ Will save userInfo. Name: Alexander
! UserInfo changed: Optional("Alexander")
A 'NSFetchedResultsController' ist eine einfache Lösung. Ich bin nicht klar, wie viel einfacher du es willst. Ich sehe aus, als hättest du schon eine Lösung, die funktioniert, was ist deine Frage? –
Danke! Was ich will - eine Lösung, die funktioniert wie die bestehende, aber mit weniger Code. – Vlad