← All courses

Persistence: @AppStorage & SwiftData

🗓 May 31, 2026 ⏱ 2 min read

@AppStorage for simple settings

For small preferences (dark mode, the chosen language, a flag), @AppStorage reads and writes UserDefaults and updates the UI automatically — it’s like @State that persists across launches.

struct SettingsView: View {
    @AppStorage("darkMode") private var darkMode = false
    var body: some View {
        Toggle("Dark mode", isOn: $darkMode)   // saved automatically
    }
}

Whenever the toggle changes, the value is persisted; the next launch reads it back. No manual save/load code.

SwiftData for real, structured data

For collections of objects that must persist (notes, tasks, a cached list), Apple’s SwiftData (iOS 17+) is the modern solution. You mark a class with @Model and SwiftData stores it in a local database — no SQL, very little boilerplate.

import SwiftData

@Model
class Task {
    var title: String
    var isDone: Bool
    init(title: String, isDone: Bool = false) {
        self.title = title
        self.isDone = isDone
    }
}

Setting up the container

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

Querying and modifying data

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

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

@Query automatically fetches and keeps the list in sync — insert or delete a task and the UI updates instantly, with no manual reload.

Choosing the right tool

  • A few flags/settings → @AppStorage.
  • Structured, growing data you query → SwiftData (or Core Data on older iOS).
  • Secrets (tokens, passwords) → the Keychain (never UserDefaults).

Common mistakes

  • Storing large or sensitive data in @AppStorage/UserDefaults.
  • Forgetting .modelContainer setup, so SwiftData queries fail.
  • Manually reloading lists instead of letting @Query stay in sync.
Summary: Use @AppStorage for simple persisted settings and SwiftData (@Model + @Query + modelContainer) for real structured data that stays in sync with your UI automatically.