LimitedRound 2 is open. Get your first month free, no extra charge.Join the waitlist ›
Wire in your backend

How to use SwiftData for data persistence

TL;DR. SwiftData is Apple's modern persistence framework for SwiftUI, introduced in iOS 17. You mark a class with @Model, attach a model container, and read live results with @Query. It stores data on device with no keys or backend, and can sync through CloudKit. AppFlight generates the models and wires the container in.

What SwiftData does for an iOS app

SwiftData stores your app's structured data on device and keeps SwiftUI views in sync with it. A class marked @Model becomes a persisted type, and a @Query in a view returns its objects and refreshes automatically when they change. Inserts, deletes, and edits go through a model context, and the framework handles the underlying store, which is built on Core Data. Because the data is local, there is nothing to host and no credential to ship. When you want the same data on a user's other devices, you enable CloudKit sync.

Adding SwiftData with AppFlight

SwiftData is part of the SDK, so there is no package or key to add. In AppFlight you describe the data you want to store, and the AI generates @Model classes and attaches a model container with the .modelContainer modifier. There are no secrets involved, so nothing is classified as client or backend. If you ask for iCloud sync, AppFlight enables the iCloud capability and configures the container to mirror to CloudKit, which keeps a user's data in their own account.

A SwiftUI example

Define a model, attach a container, and read and write through @Query and the context:

import SwiftUI
import SwiftData

@Model
final class Task {
    var title: String
    var isDone: Bool
    var createdAt: Date

    init(title: String) {
        self.title = title
        self.isDone = false
        self.createdAt = Date()
    }
}

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup { TaskListView() }
            .modelContainer(for: Task.self)
    }
}

struct TaskListView: View {
    @Environment(\.modelContext) private var context
    @Query(sort: \Task.createdAt, order: .reverse) private var tasks: [Task]

    var body: some View {
        List(tasks) { task in
            Text(task.title)
        }
        .toolbar {
            Button("Add") {
                context.insert(Task(title: "New task"))
            }
        }
    }
}

The list updates the moment you insert or delete a Task, because @Query observes the store. To delete, call context.delete(task).

Alternatives

Core Data remains the option when you need to support iOS versions before 17 or want detailed migration control, and SwiftData can interoperate with it. CloudKit on its own suits raw key-value style records synced through iCloud without a local model layer. Firestore or Supabase are the choice when data must be shared across accounts or queried on a server. For a new iOS 17 app that stores data per device, SwiftData is the simplest correct default.

FAQ

What iOS version does SwiftData need?

SwiftData requires iOS 17 or later. If you need to support older versions, Core Data is the fallback, and the two can coexist on the same store since SwiftData is built on the Core Data engine.

Does SwiftData sync to the cloud?

On its own SwiftData is a local store. You can turn on CloudKit sync so the same data follows a user across their devices through their iCloud account, with no server keys to manage.

SwiftData or Core Data?

SwiftData is the modern, far less verbose API and the right default for new iOS 17 apps. Core Data still makes sense for complex migrations, fine-grained control, or supporting iOS versions before 17.

Sources

Build this app without opening Xcode.

AppFlight turns a plain-English prompt into a real native iOS app and ships it to the App Store. Round 2 is open: free for your first month.

Join the waitlist