I want to start learning to use NSAsynchronousFetchRequest
by referring to https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/
I have the simplest use case of NSAsynchronousFetchRequest
NSAsynchronousFetchRequest
// Call from UI main thread
func X() {
let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote")
let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in
guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return }
}
let coreDataStack = CoreDataStack.INSTANCE
// backgroundContext created via persistentContainer.newBackgroundContext()
let backgroundContext = coreDataStack.backgroundContext
backgroundContext.perform {
do {
try backgroundContext.execute(asynchronousFetchRequest)
} catch let error {
backgroundContext.rollback()
error_log(error)
}
}
}
However, running the above code will get me the following error
CoreData`+[NSManagedObjectContext
Multithreading_Violation_AllThatIsLeftToUsIsHonor]:
If I modify the code by using NSFetchRequest
directly.
NSFetchRequest
// Call from UI main thread
func X() {
let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote")
let coreDataStack = CoreDataStack.INSTANCE
// backgroundContext created via persistentContainer.newBackgroundContext()
let backgroundContext = coreDataStack.backgroundContext
backgroundContext.perform {
do {
let nsPlainNotes = try fetchRequest.execute()
} catch let error {
backgroundContext.rollback()
error_log(error)
}
}
}
Thing works fine. May I know, what’s wrong with my NSAsynchronousFetchRequest
version of code?
This is my CoreDataStack.swift
for reference purpose.
CoreDataStack.swift
import CoreData
class CoreDataStack {
static let INSTANCE = CoreDataStack()
private init() {
}
private(set) lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "wenote")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// This is a serious fatal error. We will just simply terminate the app, rather than using error_log.
fatalError("Unresolved error (error), (error.userInfo)")
}
})
// So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
// persistent store.
container.viewContext.automaticallyMergesChangesFromParent = true
// TODO: Not sure these are required...
//
//container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
//container.viewContext.undoManager = nil
//container.viewContext.shouldDeleteInaccessibleFaults = true
return container
}()
private(set) lazy var backgroundContext: NSManagedObjectContext = {
let backgroundContext = persistentContainer.newBackgroundContext()
// Similar behavior as Android's Room OnConflictStrategy.REPLACE
backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
// TODO: Not sure these are required...
//backgroundContext.undoManager = nil
return backgroundContext
}()
}
Additional information
Do note that, in NSAsynchronousFetchRequest
example, even if backgroundContext.perform
is not used.
// Call from UI main thread
func X() {
let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote")
let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in
guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return }
}
let coreDataStack = CoreDataStack.INSTANCE
// backgroundContext created via persistentContainer.newBackgroundContext()
let backgroundContext = coreDataStack.backgroundContext
do {
try backgroundContext.execute(asynchronousFetchRequest)
} catch let error {
backgroundContext.rollback()
error_log(error)
}
}
Same fatal error still occur.
Please note that, this fatal error will only be triggered, by editing the schema with Arguments Passed On Launch
-com.apple.CoreData.ConcurrencyDebug 1
I even try to execute some simple project from https://github.com/abhishekbedi1432/Core-Data-Asynchronous-Fetching/tree/master which is using NSAsynchronousFetchRequest
.
If I do not enable -com.apple.CoreData.ConcurrencyDebug 1
, the sample project from github able to perform asynchronous fetch without issue. However, once the -com.apple.CoreData.ConcurrencyDebug 1
is enabled, it will also be getting the same fatal error.
2
Answers
You have to make a new context either a child or a root parent for present Core Data container, like so:
Exhaustive explanation of using multiple contexts is here.
For me, the async fetch request works as expected when I provide an
estimatedResultCount
value to it.