WWDC Notes: Bring Core Data concurrency to Swift and SwiftUI

Published on: June 10, 2021

Persistence 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

Sample app

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.

Categories

WWDC21 Notes

Subscribe to my newsletter