How to configure Core Data to work with SwiftUI
How to configure Core Data to work with SwiftUI êŽë š
Updated for Xcode 15
If you create a new project and check both SwiftUI and Core Data, Xcode does a pretty good job of getting you towards a working configuration. Specifically, it:
- Creates an empty
YourProjectName.xcdatamodeld
model file with an example configuration. - Adds a
Persistence.swift
file that wraps up Core Data neatly in one place. - Injects the context into the initial content viewâs environment using the
managedObjectContext
key. - Provides sample code in
ContentView
to create, read, and delete example data.
That provides for us the complete ability to use Core Data fetch requests from within SwiftUI.
However, if you didnât use the Core Data template or youâre just curious what the Core Data template does for us, itâs worth covering briefly the steps it takes to set up support in your apps. Iâm also going to provide you with some sample data to work with so you can try out subsequent chapters in this book.
The first step is to create a Core Data model by pressing Cmd+N to make a new file, then choosing Data Model. The name of this model matters, because it will be used in your code shortly. Unless you plan to use a complex Core Data configuration, naming your model Main is fine â thatâs the name Iâll be using here. Once you have your model, you can go ahead and create any entities you want to use in your app.
For example purposes we need some consistent data to work with so that I can give you meaningful code to work. So, open your xcdatamodeld file and create an entity called ProgrammingLanguage that has two string attributes: ânameâ and âcreatorâ. Obviously you donât need to have exactly this entity and attribute collection, so just mentally replace my examples with your own Core Data setup as you go.
Second, you need somewhere to load and manage your Core Data configuration. Appleâs template does this with a PersistenceController
singleton, which is a nice solution because it does just enough to get Core Data up and running while also providing the ability to make preview contexts for SwiftUI.
So, make a new file called PersistenceController.swift
and give it this code:
struct PersistenceController {
// A singleton for our entire app to use
static let shared = PersistenceController()
// Storage for Core Data
let container: NSPersistentContainer
// A test configuration for SwiftUI previews
static var preview: PersistenceController = {
let controller = PersistenceController(inMemory: true)
// Create 10 example programming languages.
for _ in 0..<10 {
let language = ProgrammingLanguage(context: controller.container.viewContext)
language.name = "Example Language 1"
language.creator = "A. Programmer"
}
return controller
}()
// An initializer to load Core Data, optionally able
// to use an in-memory store.
init(inMemory: Bool = false) {
// If you didn't name your model Main you'll need
// to change this name below.
container = NSPersistentContainer(name: "Main")
if inMemory {
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { description, error in
if let error = error {
fatalError("Error: \(error.localizedDescription)")
}
}
}
}
The in-memory store part of that code is important, because when you configure Core Data to save information into memory rather than disk it means all the changes you make get thrown away when your program ends.
Third, add a save()
method to your PersistenceController
class so that it checks whether the context has changes and commits them if needed.
func save() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Show some error here
}
}
}
Fourth, you need to inject the managed object context for your Core Data container into the SwiftUI environment.
This takes two smaller steps, both in the YourProjectNameApp.swift
file. First, give your app struct a property to store the persistence controller:
let persistenceController = PersistenceController.shared
And then use the environment()
modifier to attach your new Core Data view context to the managedObjectContext
key in the environment:
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
The final step is optional, but recommended: when your app moves to the background, you should call the save()
method we wrote a moment ago so that Core Data saves your changes permanently.
In SwiftUI this is done by adding a property to our app struct to monitor the scene phase:
@Environment(\.scenePhase) var scenePhase
Then you can watch for that changing and call save()
every time:
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
.onChange(of: scenePhase) { _ in
persistenceController.save()
}
Done!
Important
those instructions matter!
To avoid confusion, I want to repeat that the instructions above are important for setting up a useful Core Data environment for SwiftUI. All subsequent Core Data chapters assume you have followed the instructions above.