WWDC Notes: Bring Core Data concurrency to Swift and SwiftUI
Published on: June 10, 2021Persistence everywhere
Core Data takes care of many complexities to persist data. It converts in-memory graph to persisted data and takes care of all kinds of complex tasks like memory management.
Core Data works on all platforms, and it’s great in Swift. Apple’s been working to make Core Data better with Swift over the years.
Core Data has always cared about running code concurrently.
Swift concurrency
The sample app loads data from the background and persist it. Eventually it updates the view context.
Insertion is done with bgctx.performAndWait()
and a batch insert.
performAndWait
will block the calling thread to run the provided code on the context’s thread. The perform
function doesn’t block and runs the code on the contexts queue.
You can use await ctx.perform {}
to suspend the current execution context, have Core Data run code on its queue, and hand back control. Its usage is the same but intent is clearer without blocking.
In iOS 15, perform
is generic over the returned type of object and the supplied closure can throw. This is nice and inline with async / await .
try await ctx.perform {
throw SomeError.case
}
We used to obtain results like this:
var count: Int!
ctx.perform {
// configure a request with NSCountResultTyoe
count = request.execute().last!
}
With async / await we can do:
let count = try await ctx.perform {
// configure a request with NSCountResultTyoe
return request.execute().last!
}
This is much nicer to read and reason about.
Returning a managed object from a perform
can be risky. When you return a managed object from perform
it’s not save to interact with the returned object.
Instead, you should return a managed object ID or dict representation.
Perform is scheduled with .immediate
by default. It behaves a lot like an async version of performAndWait
.
If you use enqueued
, the work is always added to the end of the queue of the context. Even if you’re already in the correct context.
Translation to async await:
performAndWait
==await perform
perform
==await perform(.enqueued)
NSPersistentContainer
and NSPersistentStoreCoordinator
can also perform work in their context and have received similar async features.
Existing debugging tools still work and should be used.
CloudKit sharing is new and Core Data spotlight integration is improved.
Persistent stores are where data is stored. Core Data supplies XML, Binary, In Memory, and SQLite. On iOS 14 and below we used long names. Now they’re Swfty. .xml
, .sqlite
, etc.
AttributeType
is also improved.
SwiftUI
@FetchRequest
now has lazy entity resolution and they pick up dynamic configuration. There’s also a new sectioned fetch request.
Lazy entity resolution means you don’t have to set up the Core Data stack when creating your fetch request. Instead, it needs to be set up when the request is performed.
Predicates and Sort Descriptors can now be updated through properties on the wrapped value of the fetch request. Updating the property automatically reloads.
There’s also a new SortDescriptor
type that’s easier to use.
@SectionedFetchRequest
takes a sectionIdentifier
that’s used to determine which section items are in. This fetch request returns a 2D array that represents sections with items for a section as its nested result.
The sectionIdentifier
key path can be dynamically adjusted as well.
When changing a sortDescriptor
, you might have to change the sectionIdentifier
too.