Integrate Core Data With SwiftUI
Written by Team Kodeco
Core Data is a framework that manages an object graph, offering powerful, out-of-the-box solutions for data storage and management. With SwiftUI, integration has become even more straightforward. In this chapter, you’ll explore how to integrate Core Data with SwiftUI through a bookshelf app example.
Setting Up Core Data
Before we dive into SwiftUI, let’s set up Core Data. In a new Xcode project, select Use Core Data when creating the project, or manually add a Data Model file to your project.
Creating the Entity
- Open the .xcdatamodeld file in your project.
- Click Add Entity and name it Book.
-
Add attributes for your book. For this example, we’ll use
title
(String) andauthor
(String).
Here’s what this looks like in Xcode:
Core Data Stack
By default, Xcode creates the necessary code for the Core Data stack. You should see this as a file in your project named Persistence.swift, which contains a PersistenceController
, as well as lines similar to the following in your main app file:
@main
struct BookshelfApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
This creates persistenceController
and injects it into the environment so it can be used throughout your app.
If you added Core Data to an existing project, you may need to add the following to your main app file. This code sets up a persistent container that handles the loading and saving of your Core Data objects.
import CoreData
class PersistenceController: ObservableObject {
let container = NSPersistentContainer(name: "Book")
static let shared = PersistenceController()
private init() {
container.loadPersistentStores { description, error in
if let error = error {
print("Core Data failed to load: \(error.localizedDescription)")
}
}
}
}
@main
struct BookshelfApp: App {
let persistenceController = PersistenceController()
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
Building SwiftUI Views with Core Data
Now, let’s create SwiftUI views that interact with Core Data.
The ContentView
displays all the books from the Core Data store.
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: Book.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Book.title, ascending: true)]) var books: FetchedResults<Book>
@State private var isAlertModalPresented = false
@State private var bookTitleInput = ""
@State private var bookAuthorInput = ""
var body: some View {
NavigationStack {
List {
ForEach(books, id: \.self) { book in
VStack(alignment: .leading, spacing: 8) {
Text(book.title ?? "Unknown Title")
.font(.headline)
Text(book.author ?? "Unknown Author")
.font(.subheadline)
}
}
.onDelete(perform: deleteBook)
}
.navigationTitle("Bookshelf")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: { isAlertModalPresented.toggle() }) {
Image(systemName: "plus")
}
}
}
.alert("Add a Book", isPresented: $isAlertModalPresented) {
TextField("Book Title", text: $bookTitleInput)
TextField("Book Author", text: $bookAuthorInput)
Button("OK", action: addBook)
Button("Cancel", role: .cancel, action: cleanupInputs)
}
}
}
func addBook() {
let newBook = Book(context: self.moc)
newBook.title = bookTitleInput
newBook.author = bookAuthorInput
do {
try self.moc.save()
} catch {
print(error.localizedDescription)
}
cleanupInputs()
}
func deleteBook(at offsets: IndexSet) {
for index in offsets {
let book = books[index]
moc.delete(book)
}
do {
try moc.save()
} catch {
print(error.localizedDescription)
}
}
private func cleanupInputs() {
bookTitleInput = ""
bookAuthorInput = ""
}
}
struct ContentView_Previews: PreviewProvider {
static let persistenceController = PersistenceController.shared
static var previews: some View {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
You’ll need to run the simulator or your device for this example to work.
Here’s what your preview should look like:
Let’s break down the code:
-
Fetching Books: Using the
@FetchRequest
property wrapper, you fetch all theBook
objects from Core Data, sorted by title. -
Displaying Books: The books are displayed in a list using SwiftUI’s
List
andForEach
. -
Adding and Deleting Books: The
addBook
anddeleteBook
functions demonstrate how to add and delete Core Data objects. You usemoc.save()
to save changes to the persistent store. - You use an
alert
modifier to display a modal view for adding books. ThecleanupInputs
function resets the input fields after adding a book.
Integrating Core Data with SwiftUI allows for powerful data management capabilities with minimal code. Through this bookshelf example, you’ve seen how to define entities, fetch data and manipulate objects within SwiftUI views. Whether for complex applications or simple data storage, Core Data and SwiftUI offer a robust solution that every iOS developer should consider mastering.