Core Data with SwiftUI Tutorial: Getting Started
In this Core Data with SwiftUI tutorial, you’ll learn to persist data in an app using @State, @Environment and @FetchRequest property wrappers. By Keegan Rush.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Core Data with SwiftUI Tutorial: Getting Started
20 mins
- Getting Started
- Testing FaveFlick’s Persistence
- Setting Up Core Data
- Adding the Core Data stack
- Creating the Data Model
- Relationships and Fetched Properties
- Removing the Old Movie Struct
- Using the New Movie Entity
- Using an Entity’s Attributes in a View
- Using Environment to Access Managed Object Context
- Fetching Objects
- Predicates
- Testing the Results
- Deleting Objects
- Where to Go From Here?
Imagine jotting down something important in Notes, only to find your data is gone the next time you open the app! Fortunately, persistence is in excellent hands on iOS. All your notes, photos and other data are safe, thanks to Core Data. :]
There are a number of different technologies you can use when you need to store data across app launches. Core Data is the go-to solution on iOS. With impressive performance and a broad set of features, Apple’s Core Data framework manages the entire model layer of your app and handles persistence to your device’s storage disk.
In this Core Data with SwiftUI tutorial, you’ll refactor an app to add persistence and prevent the nightmare of losing your data when the app restarts. Along the way, you’ll learn to:
- Set up Core Data in a project.
- Use SwiftUI’s data flow to access what you need in the Core Data framework.
- Define and create new model objects using Core Data.
- Use Fetch Requests to retrieve objects from disk.
So buckle up and learn more about Core Data’s capabilities and how it works!
Getting Started
To start, download the project materials using the Download Materials button at the top or bottom of this tutorial. Then, open the starter project in Xcode. Then build and run.
Welcome to FaveFlicks, your own personal tally of your favorite movies. It’s a simple app that lets you add or delete a movie to the list. However, it has one glaring problem.
Yes, you guessed it right: The app does not persist data! This means that if you add some movies to your list, then restart the app, those movies you carefully added have gone. :[
Testing FaveFlick’s Persistence
To remove a movie from the list, swipe left and tap Delete.
Next, tap the Plus button on the top-right to add one of your favorites.
You’ll see the Add Movie screen.
Each Movie
object exists only in memory. They aren’t stored to disk, so closing the app removes your changes and reverts to my tasteful list of favorite movies.
Force close the app to test its persistence. With the app in foreground, enter the fast app switcher. To do this, gently drag up from the bottom of the screen. If your device has one, double-tap the Home button to enable the fast app switcher.
Now, select FaveFlicks and swipe up to close the app. On the home screen, tap FaveFlicks to open it again.
Notice that your changes are gone, and the default movies are back.
It’s time to fix that. Begin by setting up Core Data.
Setting Up Core Data
Before you start setting up persistence, you should learn about the moving parts of Core Data, also known as the Core Data stack. The Core Data stack includes:
- A managed object model which defines model objects, called entities, and their relationships with other entities. Think of it as your database schema. In FaveFlicks, you’ll define the
Movie
entity as part of the managed object model inside FaveFlicks.xcdatamodeld. You’ll use the NSManagedObjectModel class to access your managed object model in the code. - An NSPersistentStoreCoordinator, which manages the actual database.
- An NSManagedObjectContext, which is an in-memory scratchpad that lets you create, edit, delete or retrieve entities. Usually, you’ll work with a managed object context when you’re interacting with Core Data.
With that out of the way, it’s time to get started!
Adding the Core Data stack
While it might seem daunting to set up the entire Core Data stack, it’s easy thanks to NSPersistentContainer
. It can create everything for you. Open SceneDelegate.swift and add the following after import SwiftUI
:
import CoreData
Core Data lives in its own framework, so you must import it in order to use it.
Now, add the following at the end of SceneDelegate
:
// 1
lazy var persistentContainer: NSPersistentContainer = {
// 2
let container = NSPersistentContainer(name: "FaveFlicks")
// 3
container.loadPersistentStores { _, error in
// 4
if let error = error as NSError? {
// You should add your own error handling code here.
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
return container
}()
Here’s what that does:
- Add a
lazy
property calledpersistentContainer
to yourSceneDelegate
. The first time you reference the property, it will create anNSPersistentContainer
. - Create a container named
FaveFlicks
. You’ll see a file called FaveFlicks.xcdatamodeld if you look in the app’s list of files in the Project navigator. This file is where you will later design your Core Data model schema. The name of this file needs to match the name of the container. - Instruct the container to load the persistent store, which simply sets up the Core Data stack.
- If an error occurs, it’s logged and the app is killed. In a real app, you should handle this by showing a dialog indicating the app is in a weird state and needs reinstalling. Any errors here should be rare and result from developer mistakes, so you’ll catch them before submitting your app to the App Store.
That’s it. That’s all you need to set up Core Data stack. Quite a journey, huh? :]
You’re going to also need a way of saving any data to disk, because Core Data does not handle that automatically. Still in SceneDelegate.swift, add the following method at the end of the class:
func saveContext() {
// 1
let context = persistentContainer.viewContext
// 2
if context.hasChanges {
do {
// 3
try context.save()
} catch {
// 4
// The context couldn't be saved.
// You should add your own error handling here.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
This creates a method called saveContext()
which does the following:
- Obtain the
viewContext
of the persistent container. This is a special managed object context which is designated for use only on the main thread. You’ll use this one to save any unsaved data. - Save only if there are changes to save.
- Save the context. This call may throw an error, so it’s wrapped in a
try/catch
. - In the event of an error, it’s logged and the app killed. Just like in the previous method, any errors here should only happen during development, but should be handled appropriately in your application just in case.
Now that you have the Core Data stack set up and a method to save changes, it’s time to wire this into the rest of the app.
Now, in scene(_:willConnectTo:options:)
, replace let contentView = MovieList()
with the following:
let context = persistentContainer.viewContext
let contentView = MovieList().environment(\.managedObjectContext, context)
This simply grabs the same viewContext
you used earlier and sets it as an environment variable on the MovieList
SwiftUI view. The view will use this later to add and remove movies from the Core Data store.
Now add the following method to the end of SceneDelegate
:
func sceneDidEnterBackground(_ scene: UIScene) {
saveContext()
}
This instructs the app to call the save method you previously added when the app goes into the background. This is a good time to save data to disk. Later on, you’ll see how to save more frequently.
Build and run to check that the app still works. There are no functional changes just yet!