Hi everyone,
I'm trying to make use of a background actor in my SwiftUI project. Inserting data works with the ModelContainer
's mainContext.
Another context in a ModelActor
, however, fails to write into the same database.
I verify the results by opening the SQLite file on the file system. While the mainContext.insert
call does indeed insert a row into the table, the ModelActor
's context fails to do so.
There is no error or message received in the ModelActor.
The property autosaveEnabled
is set to true
.
I wrote a sample project to reproduce the issue in isolation. It consists of a single source file that introduces the Model ToDo
, the ToDoView
, initializes the ModelContainer
and ModelActor
. Please find the source code below.
Is there any mistake in my approach?
import SwiftUI
import SwiftData
@Model
final class ToDo {
let title: String
init(title: String) {
self.title = title
}
}
@main
struct testSwiftDataApp: App {
@State
var modelContainer: ModelContainer
@State
var backgroundData: BackgroundDataActor
init() {
let modelContainer: ModelContainer =
try! ModelContainer(for: ToDo.self)
self.modelContainer = modelContainer
self.backgroundData = BackgroundDataActor(
modelContainer: modelContainer
)
}
var body: some Scene {
WindowGroup {
ToDoView(backgroundData: self.backgroundData)
.modelContainer(modelContainer)
}
}
}
struct ToDoView: View {
@Environment(\.modelContext)
var modelContext
@Query
var todos: [ToDo]
let backgroundData: BackgroundDataActor
var modelContainer: ModelContainer {
self.modelContext.container
}
var body: some View {
VStack {
Text("Add ToDo")
TextField(
"",
text: .constant(
"URL to database: " +
"\(self.modelContainer.configurations.first!.url)"
)
)
// This action will be invoked on the ModelActor's context
Button {
Task {
let todo = ToDo(title: "Step 1")
await self.backgroundData.store(
id: todo.persistentModelID
)
}
} label: {
Text("Create ToDo in background")
}
// This action will be invoked on the mainContext
Button {
Task {
let todo = ToDo(title: "Step 2")
self.modelContainer.mainContext.insert(todo)
}
} label: {
Text("Create ToDo in foreground")
}
// Show the query results
VStack {
Text("Available ToDos")
ForEach(self.todos) {
Text($0.title)
}
}
}
}
}
@ModelActor
actor BackgroundDataActor: ModelActor {
func store(id: PersistentIdentifier) {
print("Trying to save")
print("Is auto save enabled: \(self.modelContext.autosaveEnabled)")
if let dbo = self[id, as: ToDo.self] {
self.modelContext.insert(dbo)
try! self.modelContext.save()
print("Saved into database")
}
}
}