问题
I'm having a problem saving things in the background. So I have 2 operations that I chain with RxSwift. The first items are saved and when I fetch them on a main context all the information is there. But in the second operation I fetch the same object and I update it with more information. Now whatever I do there it never updates the entity.
This is my coredata code:
class CoreDataStack {
static let modelName = "Mo_Application"
static let model: NSManagedObjectModel = {
let modelURL = Bundle(for: CoreDataStack.self).url(forResource: modelName, withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
lazy var mainContext: NSManagedObjectContext = {
return self.storeContainer.viewContext
}()
lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: CoreDataStack.modelName, managedObjectModel: CoreDataStack.model)
container.loadPersistentStores { (_, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
return container
}()
func newDerivedContext() -> NSManagedObjectContext {
return self.storeContainer.newBackgroundContext()
}
func saveContext() throws {
try? self.saveContext(mainContext)
}
func saveAndWaitContext(_ context: NSManagedObjectContext) throws {
if context != mainContext {
try self.saveAndWaitDerivedContext(context)
return
}
context.performAndWait {
try? context.save()
}
}
func saveAndWaitDerivedContext(_ context: NSManagedObjectContext) throws {
context.performAndWait {
try? context.save()
try? self.saveAndWaitContext(self.mainContext)
}
}
}
What I do in the first operation:
let context = coreDataStack.newDerivedContext()
let object = Foo(context: context)
object.name = "Name 1"
try coreDataStack.saveAndWaitDerivedContext(context)
maybe(.success(self)) (RxSwift to complete)
In the second operation I first fetch the object:
let context = coreDataStack.newDerivedContext()
let fetchRequest: NSFetchRequest<Foo> = Foo.fetchCreatedBy(user: user)
let objects: [Foo] = try context.fetch(fetchRequest)
let object = objects.first { $0.id == self.id }
if let object = object {
object.name = "Name updated"
}
try coreDataStack.saveAndWaitDerivedContext(context)
maybe(.success(self))
When I now do a fetch on my mainContext the old value is still there.
When I call my saveAndWaitDerivedContext the background context hasChanges. But when I then save the mainContext it doesn't have changes? Is that normal?
What am I doing wrong?
回答1:
From the documentation on NSPersistentContainer.newBackgroundContext()
:
This new context will be associated with the
NSPersistentStoreCoordinator
directly and is set to consumeNSManagedObjectContextDidSave
broadcasts automatically.
This gives a hint that your background context writes directly into the persistent store. Main context will not know about this if you don't merge the changes made in the background context into it.
You should listen to NSManagedObjectContextDidSave
notification from the background context, and merge changes from it into the main context.
func saveAndWaitDerivedContext(_ context: NSManagedObjectContext) throws {
let observer = NotificationCenter.default.addObserver(forName: .NSManagedObjectContextDidSave, object: context, queue: .main) { (notification) in
self.mainContext.mergeChanges(fromContextDidSave: notification)
}
context.performAndWait {
try? context.save()
}
NotificationCenter.default.removeObserver(observer)
}
回答2:
As the previous answer said, because newBackgroundContext()
function of NSPersistentContainer
returns a new context that will be associated with the NSPersistentStoreCoordinator
directly, you can tell your main context to automaticaly merge changes saved to its persistent store by setting automaticallyMergesChangesFromParent
property to true
:
lazy var mainContext: NSManagedObjectContext = {
self.storeContainer.viewContext.automaticallyMergesChangesFromParent = true
return self.storeContainer.viewContext
}()
As the automaticallyMergesChangesFromParent documentation states:
A Boolean value that indicates whether the context automatically merges changes saved to its persistent store coordinator or parent context.
If the above code doesn't work, maybe you should set this property after NSPersistentContainer
loads its persistent stores:
lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: CoreDataStack.modelName, managedObjectModel: CoreDataStack.model)
container.loadPersistentStores { (_, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
return container
}()
来源:https://stackoverflow.com/questions/63752475/coredata-backgroundcontext-save-doesnt-save-and-is-lost