In my conversations with developers, I’ve heard a pretty common theme from them that “Core Data is hard” or “Core Data is buggy” or “I could never get it to work right and gave up on it”.
I’ve spent a lot of time using Core Data and thought I’d share my “Laws of Core Data”. These are a set of rules I’ve developed over time on how to use Core Data in such a way that it is almost entirely painless. When I follow these rules, I almost never have any problems using it.
Examples of NSPredicate usage.
Additionally you should almost never use
NSPersistentStoreCoordinator
’smigratePersistentStore
method but instead use the newerreplacePersistentStoreAtURL
. (you can replace emptiness to make a copy). The former loads the store into memory so you can do fairly radical things like write it out as a different store type. It pre-dates iOS. The latter will perform an APFS clone where possible.
NSFetchRequest.fetchBatchSize
Work With Swift
Set
in Swift is an immutable value type. We do not recommend making Core Data relationships typed this way despite the obvious convenience. Core Data makes heavy use of Futures, especially for relationship values. These are reference types expressed asNSSet
. The concrete instance is a future subclass however. This lets us optimize memory and performance across your object graph. Declaring an accessor asSet
forces an immediate copy of the entire relationship so it can be an immutable SwiftSet
. This loads the entire relationship up front and fulfills the Future all the time, immediately. You probably do not want that.
When Apple introduced changes to Core Data + CloudKit integration in 2019, they sold developers on a dead-simple API: add iCloud sync to your Core Data app with “as little as one line of code.” That one line, of course, is simply changing
NSPersistentContainer
toNSPersistentCloudKitContainer
and enabling a few capabilities in the project settings. Boom, done! And in fact, Apple’s “Core Data –> Host in CloudKit” SwiftUI project template does those things for you, so you’re good to go, right?
When unit testing with Core Data I like using an in-memory store for speed and ease of clean up. But I also want, at least some of the time, to test with a disk-based store. Changing the location of the test database avoids overwriting or conflicting with any application database that I might already have installed on the simulator or device.
Apple recommends adding some launch arguments and environment variables to your Xcode schemes to catch and debug Core Data problems. I’ve known about some of these for a long time others were new to me.
The old way of creating an in-memory store was to change the store type in the persistent store descriptor before loading the store. The default is
NSSQLiteStoreType
but we can switch toNSInMemoryStoreType
:
storeDescription.type = NSInMemoryStoreType
There’s nothing I can find in the documentation but Apple showed a different way during WWDC 2018:
storeDescription.url = URL(fileURLWithPath: "/dev/null")
This still uses an SQLite store but we keep it in memory instead of writing it to disk. As well as being faster this also gives us a clean store each time.
Testing Core Data has some challenges. Using an in-memory store helps but what if the operation you want to test happens asynchronously? One approach is to have the test listen for the notification Core Data sends when it saves changes.
Can we integrate some of SwiftData’s excellent design philosophies and ingenious implementations into the practical use of Core Data? This article aims to explore how to introduce elegant and safe concurrency operations similar to those of SwiftData into Core Data, implementing a Core Data version of
@ModelActor
.
Codable
with Core Data and NSManagedObjectIf you’ve ever wanted to decode a bunch of JSON data into NSManagedObject instances you’ve probably noticed that this isn’t a straightforward exercise. With plain structs, you can conform your struct to Codable and you convert the struct from and to JSON data automatically.
Core Data brings a lot of power to an app and continues to evolve, but it can have rough spots when you’re working in Swift. What if you want to save an enum pr a struct? Does it help if your data is
Codable
? What’s the best way to create Swift-friendly model classes? This session will cover techniques and gotchas for integrating Core Data with your Swift code
NSDerivedAttributeDescription
: A description of an attribute that derives its value by performing a calculation on a related attribute.
When you want to test your Core Data code, it might not be immediately obvious how you can test your Core Data store in isolation.
WWDC 2017 introduced a new concept available from iOS 11 which is persistent history tracking. It’s Apple’s answer for merging changes that come from several targets like app extensions. Whenever you change something in your Core Data database from your Share Extension, a transaction is written which can be merged into any of your other targets.