I'm calling a method with the context as parameter, within the context's perform block – is this really not legal in Swift 6?
actor MyActor {
func bar(context: NSManagedObjectContext) { /* some code */ }
func foo(context: NSManagedObjectContext) {
context.performAndWait {
self.bar(context: context)
// WARN: Sending 'context' risks causing data races; this is an error in the Swift 6 language mode
// 'self'-isolated 'context' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
// Access can happen concurrently
}
}
}
The warning appears when I call a method with a context
parameter, within the performAndWait
-block.
Background: In my app I have methods that takes in API data, and I need to call the same methods from multiple places with the same context to store it, and I do not want to copy paste the code and have hundreds of lines of duplicate code.
Is there a well-known "this is how you should do it" for situations like this?
This is related to a previous post I made, but it's a bit flimsy and got no response: https://developer.apple.com/forums/thread/770605
The compiler gives you a warning / error because NSManagedObjectContext
is not Sendable
. Swift concurrency check doesn't allow passing an un-sendable type across actors because that can trigger a race.
If you have an actor that interacts with other actos via Core Data objects, consider using NSPersistentContainer
and NSManagedObjectID
, which are sendable. For example:
actor MyActor {
private let context: NSManagedObjectContext
init(persistentContainer: NSPersistentContainer) {
context = persistentContainer.newBackgroundContext()
}
private func bar(objectID: NSManagedObjectID) {
context.performAndWait {
let object = context.object(with: objectID)
/* Work on object as needed.*/
print(object)
}
}
func foo(objectID: NSManagedObjectID) {
context.performAndWait {
self.bar(objectID: objectID)
}
}
}
You can then use the actor in the following way:
func runMyActorBar() {
Task {
let myActor = MyActor(persistentContainer: PersistenceController.shared.container)
await myActor.foo(objectID: items[0].objectID)
}
}
Best,
——
Ziqiao Chen
Worldwide Developer Relations.