A SwiftData Model cannot cross actor boundaries to avoid data races. You shouldn't make your model conform to Sendable because it still doesn't.
I am currently in the same situation and as far as I can tell the best option to migrate to Swift 6 when using SwiftData is using ModelActor
There are not as many tutorials and articles about it as other SwiftData topics but I believe this is the way to go.
Do not return SwiftData Models from your ModelActor. This may result in data races when these models are accessed from different threads. Only return sole properties and the persistentModelIDto identify models. This is what my actor looks like:
@ModelActor
actor DataHandler {
public func fetch<T>(_ descriptor: FetchDescriptor<T>) throws -> [PersistentIdentifier] {
return try self.modelContext.fetchIdentifiers(descriptor)
}
public func update<T>(_ persistentIdentifier: PersistentIdentifier, keypath: ReferenceWritableKeyPath<YourModel, T>, to value: T) throws {
guard let model = modelContext.model(for: persistentIdentifier) as? YourModel else {
// Error handling
}
model[keyPath: keypath] = value
}
public func read<T>(_ persistentIdentifier: PersistentIdentifier, keypath: ReferenceWritableKeyPath<YourModel, T>) throws -> T {
guard let result = modelContext.model(for: persistentIdentifier) as? Hoerspiel else {
// Error Handling
}
return result[keyPath: keypath]
}
// And others like delete fetchCount etc. I think you get the point
}
You can also return a struct that has the same properties as your model if you need to read properties in a synchronous context. This struct will automatically conform to Sendable if you only use standard data types like Ints and Bools etc. Otherwise make it conform to Sendable.
You will probably also have the error Passing argument of non-sendable type FetchDescriptor outside of outside main actor-isolated context may introduce data races
You can either just never modify the fetch descriptor inside the modelActor (which there is no reason to do anyway) and mark the FetchDescriptor @unchecked Senable. I think the better option is to define your fetchDescriptor, modify it to your needs and then create a let constant that copies the previous fetchDescriptor. This way it cannot be mutated and therefore no data races can occur.
I hope you were able to follow. Happy to provide clarifications or corrections if someone has found a better option.