How to use SwiftData with AppIntents

Published by malhal on

I came across this StackOverflow question How do I access the SwiftData container from outside of SwiftUI? but it turns out they needed to use the container inside an AppIntent. I knew the way to inject objects into AppIntents is via the AppDependencyManager but I’d never actually tried it with the SwiftData ModelContainer so thought I’d give it a try and it works! The code should look something like this:

import SwiftUI
import SwiftData
import AppIntents

fileprivate let modelContainer: ModelContainer = {
    ...
}() // globals in Swift are lazy!

@main
struct MyApp: App {

    init() {
        // async, @MainActor and key: are all required for the Swift 6 runtime.
        let asyncDependency: () async -> (ModelContainer) = { @MainActor in
            return modelContainer
        }
        AppDependencyManager.shared.add(key: "ModelContainer", dependency: asyncDependency)
        MyAppShortcuts.updateAppShortcutParameters()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(modelContainer) 
    }
}

Then in your intent use @Depdendency, e.g.

import AppIntents
import SwiftData

struct GetTrailInfo: AppIntent {
    @Dependency(key: "ModelContainer") // key required for Swift 6 runtime.
    private var modelContainer: ModelContainer

    func perform() async throws -> some IntentResult {
        let context = ModelContext(modelContainer)
        ...
    }
}

You can see an example of using AppDependencyManager in the Accelerating app interactions with App Intents sample, although it uses custom store and not SwiftData.

We need to be careful about lazy loading the real model container, because we don’t want it to be init if using Previews. This example takes advantage of Swift globals being lazy loaded, buy you could also use a singleton. Previews runs the App struct’s init, but it doesn’t call body. In that case we will be injecting a mock container and context into the Environment within the Preview declaration.