CoreData: CRUD With Concurrency In Swift - Part 2
Do you have performance issues with your CoreData implementation because you’re blocking the main thread with some computations? This series can help you to solve your problems and improve the user experience of your App. Second Part: Read.
Introduction
This is the second part of the series CoreData: CRUD With Concurrency In Swift
: READ.
If you didn’t read the first part, I would suggest you to read it since I introduced this series.
In this article, we are going to learn how to read some data with CoreData, using background queues—to avoid blocking the main queue.
Happy Reading!
NSAsynchronousFetchRequest
To fetch the data asynchronously in a background queue, CoreData provides the object NSAsynchronousFetchRequest
.
We can create this object passing a NSFetchRequest
argument in its constructor and then executing it thanks to the execute
method of NSManagedObjectContext
.
Moreover, the constructor of NSAsynchronousFetchRequest
has also a closure parameter, which is called when the fetch finishes. This closure has also a fetch result object as parameter–NSAsynchronousFetchResult
–to retrieve the data.
To perform this asynchronous fetch in a background queue, we must call the execute
method using a private context. As we saw in the first part of this series, we have two ways to do it:
iOS 8+
|
|
iOS 10+
|
|
Once we have our private context, we are ready to perform our fetch:
|
|
As we can see in the example above, we can retrieve the result of the fetch inside the completion closure using the property finalResult
of the object passed as parameter of this closure.
Note
Since we are using a NSFetchRequest
to create a NSAsynchronousFetchRequest
, we can also set to fetchRequest
a NSPredicate
to add a specific condition to our query and/or a NSSortDescriptor
to order the query result.
Different Queues Communication
Since we’re fetching using a private context, the completion closure will be executed in a background queue. It means that, if we want to use the data in the main queue—for tasks like updating the UI—we must pass the data from background to the main queue. To achieve it, we can use DispatchQueue.main.async {}
.
When we pass NSManagedObject
between different queues, we should pay attention.
As we can read in the official documentation here:
NSManagedObject instances are not intended to be passed between queues. Doing so can result in corruption of the data and termination of the application. When it is necessary to hand off a managed object reference from one queue to another, it must be done through NSManagedObjectID instances.
You retrieve the managed object ID of a managed object by calling the objectID method on the NSManagedObject instance.
It means that, if we want to use the data in the main queue, we should update our dispatch queue like this:
|
|
For the lovers of higher-order functions, we can write the example above in this way:
|
|
Remember to use the keyword lazy
. In this way, the chain of higher-order functions will be combined in one function, interating the array just once. If we don’t use lazy
, we would iterate the array twice since we are using two flatMap
.
More…
NSAsynchronousFetchRequest
allows us also to receive notifications of the fetch progress. It’s an useful feature if we want to show the user some information about the fetching like a progress HUD.
If we want to receive the notification of the progress, we can set a KVO observer like in this example:
|
|
Then, we can use the KVO callback to read the new progress data:
|
|
If you’re wondering why we have to create a new Progess
object and set it as default one, the answer is that it’s the way to add a child progress—the one of NSPersistentStoreAsynchronousResult
—in the current thread. You can find more details in the official documentation
In this example, we observe completedUnitCount
just for the sake of explanation, we can observer any Progess
property.
Conclusion
We’ve just finished also our second adventure in the CoreData concurrency world. In the next article, we’ll see how to update the data in a background queue. Stay tuned!