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.