Persistence with Core Data

Reviewed & published by Brayan K

Core Data is Apple's framework for saving your app's objects on the device. You'll meet the NSManagedObjectContext, define entities, run fetch requests, drive a live list with @FetchRequest, and save changes.

Part of the free Swift course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

📊 Core Data Vocabulary

TermMeaning
EntityBlueprint for a kind of object
NSManagedObjectAn instance of an entity
ContextScratchpad of unsaved changes
NSFetchRequestA query for objects
save()Write changes to disk

1️⃣ Insert and Save

You create objects inside a context, set their attributes, then call save() to persist them. Nothing reaches disk until you save.

import CoreData

// The container builds the Core Data stack and gives you a context.
let container = NSPersistentContainer(name: "Notes")
container.loadPersistentStores { _, error in
    if let error = error { fatalError("\(error)") }
}
let context = container.viewContext

// Create a new managed object for the "Note" entity and set attributes.
let note = NSManagedObject(entity: container.managedObjectModel.entitiesByName["Note"]!,
                           insertInto: context)
note.setValue("Buy milk", forKey: "title")

// save() writes pending changes to the persistent store on disk.
do {
    try context.save()
    print("Saved!")
} catch {
    print("Save failed: \(error)")
}

2️⃣ Fetch Requests

An NSFetchRequest describes what to load. Attach an NSPredicate to filter and sort descriptors to order, then call context.fetch(request).

import CoreData

// A fetch request describes WHAT to load. NSFetchRequest names the entity.
let request = NSFetchRequest<NSManagedObject>(entityName: "Note")

// Optional: filter with a predicate and sort the results.
request.predicate = NSPredicate(format: "title CONTAINS %@", "milk")
request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]

do {
    let notes = try context.fetch(request)   // runs the query
    for note in notes {
        print(note.value(forKey: "title") as? String ?? "untitled")
    }
} catch {
    print("Fetch failed: \(error)")
}

Your turn. Fill in the request type and the fetch method.

// 🎯 YOUR TURN — replace each ___ then run in Xcode with a Core Data model.
import CoreData

// 1) Make a fetch request for the "Task" entity.
let request = ___<NSManagedObject>(entityName: "Task")   // 👉 the request type

// 2) Execute it against the context.
let results = try context.___(request)                   // 👉 the method that fetches

for task in results {
    print(task.value(forKey: "name") as? String ?? "")
}

// ✅ Prints the name of every saved Task.

3️⃣ @FetchRequest in SwiftUI

In SwiftUI, @FetchRequest loads managed objects and keeps the view in sync — add or delete an object and the list updates automatically.

import SwiftUI
import CoreData

// In SwiftUI, @FetchRequest loads managed objects and keeps the view in sync.
struct NoteList: View {
    @Environment(\.managedObjectContext) private var context

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(key: "title", ascending: true)]
    ) private var notes: FetchedResults<Note>

    var body: some View {
        List(notes, id: \.self) { note in
            Text(note.title ?? "untitled")
        }
    }
}

Now you try. Fill in the delete and save methods.

// 🎯 YOUR TURN — delete then save.
import CoreData

let request = NSFetchRequest<NSManagedObject>(entityName: "Note")
let notes = try context.fetch(request)

if let first = notes.first {
    context.___(first)        // 👉 the method that removes a managed object
    try context.___()         // 👉 the method that persists the change
    print("Deleted one note")
}

// ✅ Expected output:
//    Deleted one note

Pro Tips

Common Errors (and the fix)

📋 Quick Reference

TaskCodeResult
Get contextcontainer.viewContexta workspace
Savetry context.save()writes to disk
Build queryNSFetchRequest(entityName:)a request
Run querytry context.fetch(request)objects
Deletecontext.delete(object)marks removal
SwiftUI@FetchRequest(...)live results

Mini-Challenge: Tiny Task Store

Create a Task, save it, then fetch the unfinished ones with a predicate and print their names.

// 🎯 MINI-CHALLENGE: A tiny task store
// Using a Core Data model with a "Task" entity (name: String, done: Bool):
// 1. Create a new Task, set its name to "Write tests" and done to false.
// 2. Save the context.
// 3. Fetch all Tasks where done == false using an NSPredicate.
// 4. Print each task's name.
//
// ✅ Example output:
//    Write tests

// your code here

🎉 Lesson Complete!

Practice quiz

What is Core Data primarily used for?

  • Drawing UI
  • Persisting and managing an object graph on the device
  • Making network calls
  • Animating views

Answer: Persisting and managing an object graph on the device. Core Data manages and persists a graph of model objects locally.

What does an NSManagedObjectContext represent?

  • A scratchpad for creating, editing, and saving managed objects
  • A network session
  • A SwiftUI view
  • A JSON decoder

Answer: A scratchpad for creating, editing, and saving managed objects. The context is the in-memory workspace where you make and track changes before saving.

In Core Data, an 'entity' is most like...

  • A function
  • A thread
  • A table or class describing a kind of object
  • A URL

Answer: A table or class describing a kind of object. An entity defines the structure (attributes) of a type of stored object.

Which type do you use to query for objects?

  • URLRequest
  • NSFetchRequest
  • JSONDecoder
  • DispatchQueue

Answer: NSFetchRequest. NSFetchRequest describes what objects to fetch and how to filter/sort them.

Which method actually fetches results from a context?

  • context.save()
  • context.delete()
  • context.reset()
  • context.fetch(request)

Answer: context.fetch(request). context.fetch(request) executes a fetch request and returns the matching objects.

How do you persist changes to disk?

  • context.commit()
  • context.save()
  • context.flush()
  • context.write()

Answer: context.save(). Calling save() on the context writes pending changes to the persistent store.

Which property wrapper loads and observes Core Data results in SwiftUI?

  • @FetchRequest
  • @State
  • @Published
  • @Binding

Answer: @FetchRequest. @FetchRequest fetches managed objects and keeps the view updated as data changes.

To refine which objects a fetch returns, you set its...

  • timeout
  • statusCode
  • predicate (NSPredicate)
  • scheme

Answer: predicate (NSPredicate). An NSPredicate on the fetch request filters which objects match.

How do you remove a managed object?

  • context.delete(object)
  • object.remove()
  • context.drop()
  • object = nil

Answer: context.delete(object). context.delete(object) marks it for deletion; save() makes it permanent.

An NSPersistentContainer mainly provides...

  • A view hierarchy
  • A network layer
  • A JSON encoder
  • The Core Data stack including the context and store

Answer: The Core Data stack including the context and store. NSPersistentContainer sets up and holds the Core Data stack for you.

Continue this course

Frequently asked questions

What is an NSManagedObjectContext?

The context is Core Data's in-memory workspace, sometimes called a scratchpad. You create, edit, and delete managed objects within it, and it tracks every change. Nothing touches disk until you call save() on the context, at which point the pending changes are written to the persistent store. A typical app uses the container's viewContext on the main thread.

What is the difference between an entity and a managed object?

An entity is the blueprint: it defines a type of stored object and its attributes, configured in the .xcdatamodeld file. A managed object (an NSManagedObject or a generated subclass) is an actual instance of that entity living in a context. Think of the entity as the class and the managed object as an object of that class.

How do I query for stored objects?

Build an NSFetchRequest naming the entity, optionally attach an NSPredicate to filter and NSSortDescriptors to order results, then call context.fetch(request). It returns an array of matching managed objects. fetch is a throwing call, so use try inside a do/catch. The predicate format string uses Core Data's query syntax, such as 'done == NO'.

What does @FetchRequest do in SwiftUI?

@FetchRequest is a property wrapper that runs a fetch and exposes the results as a FetchedResults collection your view can iterate. Crucially it observes the context, so when objects are inserted, updated, or deleted, the view automatically refreshes. You provide sort descriptors and an optional predicate, and read the managed object context from the environment.

How do I delete an object?

Call context.delete(object) to mark a managed object for removal, then call context.save() to make the deletion permanent in the persistent store. Until you save, the deletion only exists in the context. In SwiftUI you often delete inside an onDelete handler and then save, which lets @FetchRequest update the list automatically.