It seems there are 2 ways to perform asynchronous read in CoreData, without blocking main thread UI.
newBackgroundContext + NSFetchRequest
Source : https://www.advancedswift.com/core-data-background-fetch-save-create/
// Create a new background managed object context
let context = persistentContainer.newBackgroundContext()
// If needed, ensure the background context stays
// up to date with changes from the parent
context.automaticallyMergesChangesFromParent = true
// Perform operations on the background context
// asynchronously
context.perform {
do {
// Create a fetch request
let fetchRequest: NSFetchRequest<CustomEntity>
fetchRequest = CustomEntity.fetchRequest()
fetchRequest.fetchLimit = 1
let objects = try context.fetch(fetchRequest)
// Handle fetched objects
}
catch let error {
// Handle error
}
}
newBackgroundContext + NSAsynchronousFetchRequest
Source: https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/
let privateManagedObjectContext = persistentContainer.newBackgroundContext()
// Creates a fetch request to get all the dogs saved
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog")
// Creates `asynchronousFetchRequest` with the fetch request and the completion closure
let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in
// Retrieves an array of dogs from the fetch result `finalResult`
guard let result = asynchronousFetchResult.finalResult as? [Dog] else { return }
// Dispatches to use the data in the main queue
DispatchQueue.main.async {
// Do something
}
}
do {
// Executes `asynchronousFetchRequest`
try privateManagedObjectContext.execute(asynchronousFetchRequest)
} catch let error {
print("NSAsynchronousFetchRequest error: (error)")
}
However, note that, the above code will unfortunately cause fatal error, if I were to enable flag -com.apple.CoreData.ConcurrencyDebug 1
. So far, I do not have a good solution to such. For more detail, please refer to Why I am getting Multithreading_Violation_AllThatIsLeftToUsIsHonor for this simplest NSAsynchronousFetchRequest use case?
May I know, what is the difference among newBackgroundContext + NSFetchRequest
vs newBackgroundContext + NSAsynchronousFetchRequest
?
How should I choose one over another? Thank you.
2
Answers
There are two main features of
NSAsynchronousFetchRequest
:We don’t need separated context (background, any) for it, meaning you can execute it on main view context everything else (like creating background context if needed, etc) will be done by API.
Note: you still need to redirect to main thread in completion block, because it can be called on any queue.
We can track progress of fetching data directly via
NSAsynchronousFetchResult.progress
if there isfetchLimit
set for initial request.1. About the
__Multithreading_Violation_AllThatIsLeftToUsIsHonor__
exception:There is a detailed discussion in this thread:
CoreData asynchronous fetch causes concurrency debugger error
The consensus is that it is a bug in CoreData.
There is a bug report: https://openradar.appspot.com/30692722 which is still open after 8 years at the time of writing.
2. How to properly use
NSAsynchronousFetchRequest
The API was introduced in 2014 and was discussed in this WWDC video 225_sd_whats_new_in_core_data.
It is not mentioned whether
NSAsynchronousFetchRequest
should be used on the main (view) context or on a background context.I looked over a couple of random implementations using
NSAsynchronousFetchRequest
on GitHub and I found examples for both main and background context.One thing you must do when using a background context however, is wrapping the fetch execution in in a
perform
block (documentation).In the article you linked and in the example that you excerpted above, this is missing!
This is how it should look like:
There is another potential issue with the same article, so take it with a grain of salt:
The way I understand
NSAsynchronousFetchRequest
is that it is best used from the main context and that its purpose is actually to hide the background context business from you.So:
mainContext + NSAsynchronousFetchRequest
3. How should I choose one over another?
It seems to me that
NSAsynchronousFetchRequest
was created with good intention to simplify asynchronous core data fetching for us. And to that end you may well use it to your advantage, especially if you need to deal with progress and cancellation.However I probably would not use it in my project because
One last thought – before going into asynchronous fetching at all make sure that you really need it. It may be better to optimise the performance of your query, data model or batch settings first.