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.
