ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-04-01Intermediate

Antigravity × SwiftData Practical Guide — Migrating from Core Data and Mastering the New Standard

Learn how to implement SwiftData — Apple's modern data persistence framework for iOS 17+ — with Antigravity's AI agents. Covers key differences from Core Data, basic CRUD operations, and a step-by-step migration guide with code examples.

SwiftDataCore DataiOS27Swift8Antigravity338app-dev49data persistence

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.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

App Dev2026-06-23
When StoreKit 2 Users Say They Paid but Can't Access — Field Notes on Subscription Entitlement Drift
StoreKit 2 subscriptions are harder to operate than to implement. This is a record of the drift between currentEntitlements, subscription.status, and server notifications — and the reconcile logic that finally stopped the support tickets.
App Dev2026-07-04
Paid, but the Ads Won't Go Away — Closing a StoreKit 2 Transaction.updates Launch Race with Antigravity
How I traced a StoreKit 2 launch race that dropped transactions arriving right after startup, pinned the listener to the app's lifetime instead of a view's, reconciled with currentEntitlements, and let Antigravity turn it into a StoreKitTest regression suite.
App Dev2026-05-26
Unifying In-App Review Prompts Across 5 Apps with Antigravity Editor — A Few Days of Notes
Notes from a few days spent unifying SKStoreReviewController trigger conditions across five iOS wallpaper apps I run as an indie developer, using Antigravity Editor's multi-file editing to bring the logic onto one shared coordinator.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →