Persistence (Core Data)
Persistence (Core Data)
Model, store, and query your app data with Core Data using an NSPersistentContainer and SwiftUI integration.
What is Core Data?
Core Data is Apple's object graph and persistence framework.
It manages models, relationships, and change tracking, and can persist to SQLite under the hood.
Basic Setup
Add Core Data when creating the project, or create a NSPersistentContainer manually and pass its viewContext down to your SwiftUI views via .environment(\_.managedObjectContext).
This example demonstrates a basic setup with a single entity and a list view.
Syntax:
let container = NSPersistentContainer(name: "Model")container.loadPersistentStores
Example
import SwiftUI
import CoreData
@objc(Note)
class Note: NSManagedObject {
@NSManaged var title: String?
}
extension Note {
@nonobjc class func fetchRequest() -> NSFetchRequest<Note> { NSFetchRequest<Note>(entityName: "Note") }
}
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init() {
// Programmatic model so the sample runs without an .xcdatamodeld
let model = NSManagedObjectModel()
let entity = NSEntityDescription()
entity.name = "Note"
entity.managedObjectClassName = NSStringFromClass(Note.self)
let title = NSAttributeDescription()
title.name = "title"
title.attributeType = .stringAttributeType
title.isOptional = true
entity.properties = [title]
model.entities = [entity]
container = NSPersistentContainer(name: "MyModel", managedObjectModel: model)
let description = NSPersistentStoreDescription()
description.type = NSInMemoryStoreType
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { _, error in
if let error = error { fatalError("Unresolved error: \(error)") }
}
}
}
struct BasicCoreDataView: View {
@Environment(\.managedObjectContext) private var context
@FetchRequest(sortDescriptors: []) private var items: FetchedResults<Note>
var body: some View {
List(items, id: \.objectID) { note in Text(note.title ?? "") }
.toolbar { Button("Add") { add() } }
}
private func add() {
let i = Note(context: context)
i.title = "New"
try? context.save()
}
}
import SwiftUI
struct ContentView: View {
var body: some View { BasicCoreDataView() }
}
import SwiftUI
import CoreData
@main
struct MyApp: App {
let persistence = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistence.container.viewContext)
}
}
}
This example initializes a persistent container and uses @Environment(\.managedObjectContext) with @FetchRequest to list and add items.
The note about .xcdatamodeld is for running this in Xcode. It is not relevant for this example. But if you want to use Xcode, you can add a .xcdatamodeld file to your project.
Sample App: Notes (Core Data)
This example demonstrates a more complex setup with multiple entities and relationships.
Syntax:
@Environment(\.managedObjectContext)for the context@FetchRequest(sortDescriptors: [NSSortDescriptor(...)])to querycontext.save()to persist
Example
import SwiftUI
import CoreData
class NoteEntity: NSManagedObject {
@NSManaged public var id: UUID?
@NSManaged public var title: String?
@NSManaged public var body: String?
}
extension NoteEntity: Identifiable {}
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init() {
container = NSPersistentContainer(name: "MyModel")
container.loadPersistentStores { _, error in
if let error = error { fatalError("Unresolved error: \(error)") }
}
}
}
struct NotesCoreDataView: View {
@Environment(\.managedObjectContext) private var context
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \NoteEntity.title, ascending: true)])
private var notes: FetchedResults<NoteEntity>
var body: some View {
List(notes) { n in
VStack(alignment: .leading) {
Text(n.title ?? "Untitled").font(.headline)
Text(n.body ?? "").font(.subheadline)
}
}
.toolbar { Button("Add") { add() } }
}
private func add() {
let n = NoteEntity(context: context)
n.id = UUID(); n.title = "New Note"; n.body = ""
try? context.save()
}
}
import SwiftUI
struct ContentView: View {
var body: some View { NotesCoreDataView() }
}
import SwiftUI
import CoreData
@main
struct MyApp: App {
let persistence = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistence.container.viewContext)
}
}
}
App Group Shared Store (Widget Access)
Store your Core Data SQLite file in an App Group so your Widget can read the same data.
This example demonstrates how to set up an App Group shared store.
Example
import SwiftUI
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init() {
container = NSPersistentContainer(name: "MyModel")
if let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.example.notes") {
let storeURL = groupURL.appendingPathComponent("MyModel.sqlite")
let description = NSPersistentStoreDescription(url: storeURL)
container.persistentStoreDescriptions = [description]
}
container.loadPersistentStores { _, error in
if let error = error { fatalError("Unresolved error: \(error)") }
}
}
}
struct AppGroupListView: View {
@Environment(\.managedObjectContext) private var context
@FetchRequest(sortDescriptors: []) private var items: FetchedResults<Item>
var body: some View {
List(items) { item in Text(item.title ?? "") }
.toolbar { Button("Add") { add() } }
}
private func add() {
let i = Item(context: context)
i.title = "Shared Item"
try? context.save()
}
}
import SwiftUI
struct ContentView: View {
var body: some View { AppGroupListView() }
}
import SwiftUI
import CoreData
@main
struct MyApp: App {
let persistence = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\(.managedObjectContext), persistence.container.viewContext)
}
}
}
Enable the same App Group on the Widget target, then query via a Core Data stack that points to the same container URL.
Tip: Keep heavy work off the main viewContext.
Use background contexts for imports and sync.