Consider a sample SwiftData project
var body: some View {
List(recipes) { recipe in
NavigationLink(recipe.name, destination: RecipeView(recipe))
}
}
For SwiftUI views that uses SwiftData it's very straight forward.
However, if I need to read/write to SwiftData while in the background (let's say after a network call), the app crashes.
Imagine a simple workflow where
(1) user selects some model objects (via SwiftData modelContext)
(2) now I need to pass these objects to some API server
(3) in a background thread somewhere I need to use the modelActor but it now cannot read/write to the original model objects without risk of crashing
So instead, the only way I thought of is to create a separate modelContext for background processing. There I passed down the container from the main app, and creates a new ModelActor that takes in the container and owns a new modelContext of its own.
This works, without crash. However it introduces many side effects:
(1) read/write from this modelActor seems to trigger view changes for SwiftData's modelContext. But not vice versa.
(2) model objects fetched from one context cannot be used in another context.
(3) changes made in the actor also doesn’t not automatically sync with icloud but changes in SwiftData’s modelContext do.
So I guess the bigger question here is, what is the proper usage of SwiftData in a background thread?
Yeah, a SwiftData model object is tied to a model context. If you have an object and would like to use in the other context, use the persistent model ID instead. Concretely:
// In your SwiftUI view:
let modelId = items[0].persistentModelID
Task {
...
try await myModelActor.updateData(identifier: modelId)
}
// In your actor
@ModelActor
public actor MyModelActor {
public func updateData(identifier: PersistentIdentifier) throws {
guard let model = modelContext.model(for: identifier) as? ModelActorItem else {
return
}
... // Change the object here.
try modelContext.save()
}
}
Regarding that a change made from a model actor doesn't trigger a SwiftUI update, you might check out the Importing Data into SwiftData in the Background Using ModelActor and @Query post to see if that helps.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.