[ad_1]
I have a core data store. What I want is to perform saves in a background context, and fetches on the main context. So I have the following setup of CoreDataStack
that handles the saving/fetching in the core data:
class CoreDataStack {
static let modelName = "Model"
static let managedObject: NSManagedObjectModel = {
guard let modelUrl = Bundle.module.url(
forResource: "Model",
withExtension: "momd"
) else {
return .init()
}
return NSManagedObjectModel(contentsOf: modelUrl) ?? .init()
}()
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(
name: Self.modelName,
managedObjectModel: Self.managedObject
)
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
dispatchGroup.leave()
})
dispatchGroup.wait()
return container
}()
lazy var backgroundContext: NSManagedObjectContext = {
let context = persistentContainer.newBackgroundContext()
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return context
}()
lazy var mainContext: NSManagedObjectContext = {
let mainContext = persistentContainer.viewContext
mainContext.automaticallyMergesChangesFromParent = true
mainContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return mainContext
}()
func fetch<Item: NSManagedObject>(
_ request: NSFetchRequest<Item>
) -> [Item] {
do {
let items = try self.mainContext.fetch(request)
return items
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
return []
}
}
func saveContext() {
backgroundContext.perform {
do {
try self.backgroundContext.save()
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
}
}
}
}
I have an NSManagedObject
called ManagedBar
. This contains a data
property which is an NSSOrderedSet
of another NSManagedObject
called ManagedFoo
.
I have a function saveFoo(_:)
which creates a ManagedFoo
on the background context, requests a ManagedBar
in the foreground context, then if that ManagedBar
exists, adds it to the ManagedFoo
to data
. Then the background context is saved:
func saveFoo(_ Foo: Foo) {
let managedFoo = ManagedFoo(
context: coreDataStack.backgroundContext,
name: foo.name,
)
let request = ManagedBar.fetchRequest()
request.predicate = NSPredicate(format: "name == %@", foo.name)
if let bar = coreDataStack.fetch(request).first {
bar.addToData(managedFoo)
} else {
let event = ManagedBar(
context: self.coreDataStack.backgroundContext,
name: foo.name
)
self.coreDataStack.saveContext()
}
}
I also have a function to retrieve all the name
properties of ManagedBar
which fetches them all from the main context:
func getAllBarNames() -> [String] {
let fetchRequest = ManagedEvent.fetchRequest()
let eventData = coreDataStack.fetch(fetchRequest)
let names = eventData.map { $0.name }
return names
}
Then, when I run the following code, it works most of the time but sometimes fails:
func testGetAllEventNames() {
let foo1 = Foo()
saveFoo(foo1)
let foo2 = Foo()
saveFoo(foo2)
let names = getAllBarNames()
print(names)
}
This sometimes works fine, but other times fails for one of two reasons:
Cannot save objects with references outside their own stores.
Collection <__NSCFSet: 0x6000031407b0> was mutated while being enumerated.
(this occurs on the secondsaveFoo
)
Other times, names
doesn’t contain all the names that I’ve saved. These errors happen even if I run this code in a unit test and wait for the NSManagedObjectContextDidSave
notification before calling getAllBarNames()
This must be caused by some background/main context issue but not entirely sure what. Does anyone know what’s going wrong here?
[ad_2]
Source link