I'm keeping most information in an actor and I would like to save also a closure in it that I get from
func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void)
Task.init{
await GeoreferenceQueue.shared.setBackgroundCompletionHandler(completionHandler)
}
}
where GeoreferenceQueue is and actor, while the caller is a class. yet I receive error:
Passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure
and
Sending task-isolated 'completionHandler' to actor-isolated instance method 'setBackgroundCompletionHandler' risks causing data races between actor-isolated and task-isolated uses
So, yeah, the compiler is right to complain about this. That completion handler is intended to be called from the main thread. By passing it to a newly created task you allow it to be called from other contexts, which is Not Good™.
There isn’t one true way of resolving this issue. The best option is gonna vary based on how you’ve set up your code. For example, the following is a very expedient option that kinda matches your existing design:
@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
…
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
let runCompletionHandler = { @MainActor in
completionHandler()
}
Task {
await GeoreferenceQueue.shared.addCompletionHandler(runCompletionHandler)
}
}
}
actor GeoreferenceQueue {
static let shared = GeoreferenceQueue()
func addCompletionHandler(_ h: (() async -> Void)) {
…
}
}
It works by wrapping the completion handler sync function in an async function that’s also bound to the main actor. This function is sendable because, regardless of where it’s called from, it’ll bounce to the main actor before called completionHandler. Given that, it’s fine to pass the wrapper to the GeoreferenceQueue actor.
However, that’s just one example. If I were in your shoes I’d probably move the code that keeps track of these completion handlers to a main-actor-isolated type and then loosely couple that type to GeoreferenceQueue via some sort of notification.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"