WWDC23 Core Data Lab Notes

My Core Data lab notes from WWDC23. As with my other lab notes, this is not a verbatim transcript. I’ve summarised based on my notes and memory so it’s always possible I misunderstood something.

Persistent History Tracking

When do I need to add “preserve after deletion” to a field in the model editor? I’ve never used it but wonder if I’m missing something?

Good question, I’ve also never used it. It adds the property to the entities tombstone. When you process persistent history, for example when undoing a delete, the tombstone contains the preserved value you can use to restore the entity.

CloudKit Sync

What are the end-user prerequisites for sync to work? The user must log into iCloud and enable iCloud for the app, but must they enable iCloud Drive. If so, why?

Looking at the code, it is not required.

Is there anything we can do to increase the priority of background updates to improve sync performance?

There’s no API to request more sync time, it’s managed by the system. File a feedback request if you would like to see a way to request more sync time, or control the priority of sync operations.

App extensions can struggle to get enough time to sync. Is it better to place the store in an app group and have the extension access it directly?

Yes that is possible. We released the Trips sample code this year showing a Widget using SwiftData directly accessing a store in a shared app container. Both the app and the widget load and use the same persistent store. Also an example of SwiftData and CoreData coexisting.

Using Local and Cloud Stores

Is it a good idea to have two stores. One to sync with iCloud and another that is not synced that drives the UI. The benefit being that the UI model is stable, can have required relationships, unique constraints and not depend on sync status?

It’s more complex, but there is nothing wrong with that approach. We propose a similar architecture in the docs. A cloud store using NSPersistentCloudKitContainer and a local (UI) store. The cloud store syncs the data and then you copy it to the local store.

How do you keep the cloud and local stores in sync? Do you listen to the change notifications on each store?

You can, but it can be simpler than that. If you don’t have real-time constraints you could check the change token each time you run and process any new updates on a background thread.

How about when a child is not yet connected to a parent? Do you wait for the parent record to arrive in the cloud store before processing the child?

Exactly. there’s no guarantee that the parent will arrive before the child. You’d need a step to check if this is a valid subtree, could I display this, does it have all the necessary components, before moving to the local store.

If there any documentation on the status codes, other than CKError? For example 134400 seems to happen when not logged into iCloud. I’ve also seen others in that range like 134419? Is there any action I can take when seeing such errors?

Those are private errors. 134400 happens when the NSPersistentCloudKitContainer failed to initialise. There are a lot of internal errors but you should not be seeing them. If you see them file a feedback requesting we make them public with an explanation on how to recover.


How is SwiftData related to Core Data? How much of Core Data does it use under the hood?

You shouldn’t care. I can’t say too much about it but if there are features missing from SwiftData that you had in Core Data please file a feedback.


What else is new in Core Data this year?

Composite attributes. Can be nested. A great replacement for transformable data types. Stored on the same record so doesn’t need a relationship fetch.

Deferred migration allows you to choose the best time to cleanup a migration. For example, a migration that requires cleanup of a column in the database could delay the launch of an app. With a deferred migration, the migration still happens right away but you can defer the cleanup to later.

Staged migrations. Allows you to break down a heavyweight migration into several lightweight migrations. Removes the need to use a mapping model. Each migration step can have willMigrate and didMigrate handlers.

For example, if you changed an optional attribute to non-optional, you could use the will migrate handler to fetch all entries that are nil and set them to a computed value. Another use is to denormalize a database, breaking attributes out to another entity with a relationship. That would normally need a mapping model.

See Also