How to use SwiftData with AppIntents
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.