Chapters

Hide chapters

SwiftUI Cookbook

Live Edition · iOS 16.4 · Swift 5.8.1 · Xcode 14.3.1

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
  1. Open the .xcdatamodeld file in your project.
  2. Click Add Entity and name it Book.
  3. Add attributes for your book. For this example, we’ll use title (String) and author (String).

Here’s what this looks like in Xcode:

Adding a new Core Data entity and its attributes in Xcode.
Adding a new Core Data entity and its attributes 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:

An app showing a list of books powered by Core Data.
An app showing a list of books powered by Core Data.

Let’s break down the code:

  • Fetching Books: Using the @FetchRequest property wrapper, you fetch all the Book objects from Core Data, sorted by title.
  • Displaying Books: The books are displayed in a list using SwiftUI’s List and ForEach.
  • Adding and Deleting Books: The addBook and deleteBook functions demonstrate how to add and delete Core Data objects. You use moc.save() to save changes to the persistent store.
  • You use an alert modifier to display a modal view for adding books. The cleanupInputs 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.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.