WWDC22 Core Data Lab Notes

I attended a Core Data lab at WWDC22. I wanted to check my understanding of CloudKit sync with a private store. My questions were not app-specific so I thought I would share my notes with you.

I started with some general Core Data questions, before moving on to CloudKit sync.

Note that this is not a verbatim transcript of the conversation. I’ve summarised based on my notes and memory so it’s always possible I misunderstood something.

Loading store asynchronously

Apple sample code and docs always seem to load the store synchronously. I prefer to load asynchronously to avoid blocking the UI during migrations or loading large databases. Is that still recommended?

Async is the right approach for a large database but recommended even for small databases. You need some logic to avoid making requests until Core Data has finished loading the store.

SwiftUI handling faulted objects

When observing changes to an NSManagedObject I need to guard against Core Data faulting the object. Is testing isFault the best way to protect against objects that I’ve deleted in another window/process?

My scenario is two windows on an iPad, one showing a SwiftUI list, the other showing a detail view of an observed object. Deleting the object in the list view faults the object in the detail view setting all non-scalar properties to nil.

I didn’t get a definitive answer to this question, the Apple Engineer was going to ask somebody else but we ran out of time to come back to it. It’s not clear to me if this expected behaviour or not. One suggestion was to use query generation to pin the context.

It’s also recommended to use query generation when working with fetched results controllers so that items in a list don’t change unexpectedly while the user is scrolling.

When is isDeleted set?

When should I expect the isDeleted flag to be true?

Core Data sets the isDeleted flag after you delete the object but before you save the context.

Persistent History Tracking

Do I ever need to purge the persistent history tracking data?

No. We don’t recommend it. NSPersistentCloudKitContainer uses the persistent history token to track what to sync. If you delete history the cloud sync is reset and has to upload everything from scratch. It will recover but it’s not a good customer experience.

It shouldn’t normally be necessary to delete history. For example, the Apple Photos app doesn’t trim its history, so unless you’re generating massive amounts of history don’t do it.

Enable/disable CloudKit Sync

What’s the best way to allow the user to enable/disable CloudKit sync?

No easy way. One way is to tear down the persistent stack, throwing away the CloudKit container object to stop the synch or removing the store from the coordinator. On app launch use either a regular Core Data container or the CloudKit container.

Schema Initialization

Can I safely repeatedly call initializeCloudKitSchema from a unit test?

That’s fine. You have to promote the development schema to production in the CloudKit console. Everything in the development environment is resettable.

CloudKit Dashboard Error

When creating a schema I see an error that recordName is not marked queryable. Is it normal that I need to make recordName queryable for each record?

This is a normal, manual step when setting up schema. This should carry over into production when you promote the schema.

Remote Changes

If I do not have any app extensions do I need to process remote changes?

The notification payload tells you what has changed. You can iterate over the changes and decide to refresh objects. Query generations stop the data from changing underneath you. Filter on the transaction author. It becomes more useful when you have app extensions or other processes creating changes.

Orphaned Records

Is there a risk of orphaned records when I add a record to a parent that I deleted elsewhere?

Always possible. Core Data sync uploads changes to iCloud in batches. When a device downloads changes the graph may not be fully inflated. Relationships may be nil as the other records have not yet synched.

Cloud-enabled stores have a number of restrictions. Relationships are optional. Usually we recommend having another store that is not CloudKit enabled. Copy the data from the CloudKit store to the local store where you can perform validation. Serve clients from the local store where you know the graph is valid.

Simpler approach to periodically clean up orphaned records if you don’t care about them.

Sync Status

How can I respond when a user contacts me to say sync is not working?

Collect logs, teach the user how to take a sysdiagnose. See the WWDC22 talk Optimize your use of Core Data and CloudKit.

Final Thoughts

If you’ve never attended a WWDC lab I strongly encourage you to give it a try. You only get 30 minutes and I ran out of time to ask everything or get all the answers I wanted. Prepare your questions to make best use of the time.

The opportunity to talk directly with an Apple Core Data framework engineer was invaluable to double-check my understanding. It’s an opportunity we get once a year so I recommend you take advantage of it.