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")
}
}
}