So I don't know how to perform SwiftData operations on a background thread. Every time I call my @ModelActor
, everything runs on the main thread. I've read online that you must instantiate it on a background thread, but it's still executing methods on the main thread. I've also read about executors, but couldn't find anything working.
EDIT: It seems calling a MainActor
in the .task {
introduces the issue?
Here's my code so far:
import SwiftUI
import SwiftData
@Model
class User {
var id: UUID
init(id: UUID) {
self.id = id
}
}
@MainActor
final class SomeMainActor {}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.task {
do {
let savedUserProvider = try await SavedUserProvider.createOnBackground()
let someMainActor = SomeMainActor()
await savedUserProvider.updateUser()
} catch {}
}
}
}
}
@ModelActor
actor SavedUserProvider {
static func createOnBackground() async throws -> SavedUserProvider {
await Task.detached(priority: .background) {
let container: ModelContainer = {
let schema = Schema([
User.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
return SavedUserProvider(modelContainer: container)
}.value
}
func updateUser() {
print()
// Runs on main thread
}
}
Has anyone managed to have SwiftData updates on a background thread with ModelActor ?
As @Rob suggested, prefer using a normal actor over a @ModelActor since calling it from the main thread will execute code on the main thread.
So here's the solution with a normal actor and a private executor as suggested by Rob:
actor SavedUserProvider {
private let modelExecutor: any ModelExecutor
private let modelContainer: ModelContainer
private var modelContext: ModelContext { modelExecutor.modelContext }
init(modelContainer: ModelContainer) {
self.modelExecutor = DefaultSerialModelExecutor(modelContext: ModelContext(modelContainer))
self.modelContainer = modelContainer
}
func updateUser() {
// Will never run on main thread
}
}