Apple has shown us how to perform heavy write operation using background thread (by using newBackgroundContext
) in their official earth quake example - https://github.com/yccheok/earthquakes-WWDC20
But, what about heavy read operation? (millions of rows for stress test purpose)
We would also like our app UI to be responsiveness, when we are launching the app for first time, and the app is reading a large amount of data from CoreData.
The following is the code snippet which is using NSFetchedResultController
.
UI is not responsiveness if there are a lot of rows
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: persistentContainer.viewContext,
sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = fetchedResultsControllerDelegate
// Perform the fetch.
do {
// This statement tooks some time to complete if you have a lot of rows.
try controller.performFetch()
} catch {
fatalError("Unresolved error \(error)")
}
UI is not responsiveness if there are a lot of rows
We try to perform controller.performFetch()
using background thread. Still, but not sure why, the UI is still not responsiveness. My guess is that, after NSFetchedResultsController
is occupied UI main thread, to perform some time consuming I/O read operation.
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: persistentContainer.viewContext,
sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = fetchedResultsControllerDelegate
DispatchQueue.global(qos: .background).async {
// Perform the fetch.
do {
// This statement tooks some time to complete if you have a lot of rows.
try controller.performFetch()
} catch {
fatalError("Unresolved error \(error)")
}
}
UI is responsive now. But the solution is incorrect...
I guess, we need to place NSFetchedresultController
under background thread too. Hence, we do the following modification.
let backgroundContext = persistentContainer.newBackgroundContext()
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: backgroundContext,
sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = fetchedResultsControllerDelegate
backgroundContext.perform {
// Perform the fetch.
do {
// This statement tooks some time to complete if you have a lot of rows.
try controller.performFetch()
} catch {
fatalError("Unresolved error \(error)")
}
}
The UI is responsiveness during fetching, and data is able to be fetched and shown after some time.
However, if we investigate further by using the launching argument
-com.apple.CoreData.ConcurrencyDebug 1
We will experience the following crash after controller.performFetch()
finish executed.
CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
May I know, is it ever possible to make UI responsive, when we are using NSFetchedResultController
to load a large amount of data (few millions rows as per testing)? Can NSFetchedResultController
ever operated under background thread?