SwiftData, introduced alongside iOS 17, is Apple's modern replacement for Core Data. Built on the same underlying engine, it offers a declarative Swift-native API and leverages Swift macros to eliminate much of the boilerplate that made Core Data feel heavy.
Below, we put Antigravity's AI agents to work implementing SwiftData efficiently — from a fresh project to migrating an existing Core Data app — with practical code at every step.
SwiftData vs Core Data — What's Actually Different
SwiftData targets iOS 17, macOS 14, watchOS 10, and tvOS 17 and later. While it shares Core Data's persistence engine under the hood, the developer experience is dramatically improved in several key ways.
Simplified model definition: Where Core Data required a .xcdatamodeld visual editor and code generation, SwiftData uses the @Model macro — just annotate a Swift class and you're done.
Native SwiftUI integration: The @Query macro lets you fetch data directly inside SwiftUI views, replacing the verbose @FetchRequest setup with a one-liner.
Improved concurrency: ModelActor makes background context management straightforward, aligning with Swift's structured concurrency model.
Because Antigravity deeply understands Swift syntax and patterns, it's an ideal partner for generating, refactoring, and debugging SwiftData code.
Setting Up a SwiftData Project with Antigravity
In Xcode 15 and later, you can enable SwiftData for a new project by checking the Use SwiftData option in the project creation wizard. For existing projects, the setup is manual — but Antigravity makes it painless.
Try typing this in Antigravity's chat:
I want to add SwiftData to my existing SwiftUI project.
Walk me through the files I need to modify and create.
Antigravity will scan your project structure and outline exactly where to add the modelContainer modifier to your @main entry point, and which views will be affected.
Defining Models and Performing CRUD Operations
The core of SwiftData is refreshingly concise. Here's a complete example for a task management app:
import SwiftData
// Annotate with @Model — that's all it takes to make a class persistent
@Model
final class Task {
var title: String
var isCompleted: Bool
var createdAt: Date
var priority: Int
init(title: String, isCompleted: Bool = false, priority: Int = 0) {
self.title = title
self.isCompleted = isCompleted
self.createdAt = .now
self.priority = priority
}
}Registering the Model Container
import SwiftUI
import SwiftData
@main
struct TaskApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
// Register the Task model with the app's model container
.modelContainer(for: Task.self)
}
}Fetching and Mutating Data in SwiftUI
The @Query macro handles fetching automatically — changes in the database are reflected in the UI without any manual reload.
import SwiftUI
import SwiftData
struct TaskListView: View {
// Automatically fetches tasks, sorted by creation date (newest first)
@Query(sort: \Task.createdAt, order: .reverse)
private var tasks: [Task]
@Environment(\.modelContext) private var context
var body: some View {
List {
ForEach(tasks) { task in
HStack {
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.onTapGesture {
// Mutating a property automatically persists the change
task.isCompleted.toggle()
}
Text(task.title)
.strikethrough(task.isCompleted)
}
}
.onDelete { indexSet in
for index in indexSet {
context.delete(tasks[index])
}
}
}
.toolbar {
Button("Add") {
// Insert a new task into the model context
let newTask = Task(title: "New Task")
context.insert(newTask)
}
}
}
}Notice there's no NSFetchRequest, no explicit context.save(), and no complicated binding setup. You can paste this into Antigravity and say "add a deadline property and highlight overdue tasks in red" — the AI agent will produce the exact diff you need.
Advanced Filtering and Sorting with @Query
One of the most powerful aspects of SwiftData is the #Predicate macro, which lets you express filter logic in plain Swift instead of NSPredicate's string-based syntax. Antigravity is particularly good at generating complex predicate expressions because it can reason about Swift types rather than interpreting format strings.
struct HighPriorityTasksView: View {
// Filter tasks that are incomplete and high priority
@Query(
filter: #Predicate<Task> { task in
!task.isCompleted && task.priority >= 2
},
sort: \Task.createdAt,
order: .reverse
)
private var urgentTasks: [Task]
var body: some View {
List(urgentTasks) { task in
Text(task.title)
.fontWeight(.semibold)
}
}
}You can ask Antigravity to generate these predicates by describing the logic in plain language: "show tasks created in the last 7 days that aren't completed and have a priority above 1." The AI will translate that into the correct #Predicate expression, including the appropriate date arithmetic.
Migrating from Core Data Step by Step
Migrating an existing Core Data project is where Antigravity really shines. Here's the general approach:
Step 1: Convert Your Data Model
Attach your .xcdatamodeld file to Antigravity and say: "Convert this Core Data model to SwiftData @Model classes." Antigravity will map entities, attributes, and relationships into clean Swift code.
Step 2: Create a Migration Plan
If you need to preserve existing user data, you'll need a SchemaMigrationPlan. Ask Antigravity to "write a MigrationPlan for migrating from Core Data to SwiftData," and it will generate a VersionedSchema and MigrationStage scaffold like this:
import SwiftData
enum TaskSchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] { [Task.self] }
@Model
final class Task {
var title: String
var isCompleted: Bool
var createdAt: Date
init(title: String) {
self.title = title
self.isCompleted = false
self.createdAt = .now
}
}
}
enum TaskMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] { [TaskSchemaV1.self] }
static var stages: [MigrationStage] { [] }
}Step 3: Incrementally Update Your Views
Replace @FetchRequest with @Query, and swap NSManagedObjectContext for ModelContext. Hand Antigravity each file and say "rewrite this from Core Data to SwiftData style" — it will work through the changes file by file.
If your app uses a complex layered architecture, it may be worth stepping back and reconsidering the overall structure during migration. The Antigravity Clean Architecture & DDD Implementation Guide covers this in depth.
Common Errors and How to Fix Them
"Fatal error: 'try!' expression — 'nil' unexpectedly found"
This usually means the ModelContainer failed to initialize, often because the schema changed without a migration plan. Try uninstalling the app from the simulator (to wipe the store) and rebuilding.
"@Query crashes in Xcode Previews"
SwiftUI Previews need their own model container. Use an in-memory container to keep previews lightweight and isolated:
#Preview {
TaskListView()
// In-memory container — fast and isolated, perfect for previews
.modelContainer(for: Task.self, inMemory: true)
}"Changes to relationships aren't reflected in the UI"
SwiftData relationships must be defined with @Relationship, and bidirectional relationships need to be explicitly configured on both sides. Tell Antigravity "set up a bidirectional relationship between Task and Project" and it will generate the correct annotations for both models.
If you're new to iOS development, start with the Antigravity × iOS Swift Beginner App Guide. For adding iCloud sync to your SwiftData app, the Antigravity × SwiftUI + CloudKit Fullstack Guide walks through the full implementation.
Looking back
SwiftData is a genuine improvement over Core Data — the @Model and @Query macros cut boilerplate dramatically while keeping the full power of Apple's persistence stack. Antigravity's deep understanding of Swift makes it an effective partner throughout the process, from initial model design to incremental migration and error resolution.
Whether you're starting fresh or moving an existing app, working through SwiftData with Antigravity's AI agents is one of the more enjoyable upgrade paths in recent Apple platform history.