I keep struggling with Swift 6. Currently I don't understand how are we supposed to create a class with a delegate that's able to do async work in @Sendable closures.
Small example:
I have a class Service
that has a method to trigger its implementation. It also has a delegate
to notify that the triggered method finished.
protocol ServiceDelegate: AnyObject {
func serviceDidSomething(withData: Data)
func somethingWentWrong()
}
final class Service {
weak var delegate: ServiceDelegate?
func doSomething() {
guard let url = URL(string: "www.foo.com") else { return }
Task {
do {
let (data, _) = try await URLSession.shared.data(for: .init(url: url))
delegate?.serviceDidSomething(withData: data)
} catch {
delegate?.somethingWentWrong()
}
}
}
}
Building with complete concurrency this throws a warning "Task-isolated value of type '() async -> ()' passed as a strongly transferred parameter; later accesses could race; this is an error in the Swift 6 language mode".
I don't quite understand how to approach this when it comes to @Sendable closures for several reasons:
- I can make the method async and have it return instead of notifying the delegate but that's avoiding the situation I want to solve. The one triggering the method is not necessarily the one interested in the result.
- I cannot switch this class to an actor or make it conform to Sendable. The
delegate
variable is mutable by anyone referencing this class, which is pretty much what I want and what I believe to be a common pattern. - What if
doSomething
doesn't have an alternative withasync await
and I have to use a@Sendable
closure? For example what if I want to useTimer.scheduledTimer(..., block: @escaping @Sendable (Timer) -> Void
?
I tried looking at something similar from Apple and went with URLSession
since it also has a delegate variable and does async work.
open class URLSession : NSObject, @unchecked Sendable {
....
open var delegate: (any URLSessionDelegate)? { get }
}
Yes, adding @unchecked Sendable
does make the warning go away. Is it really the correct approach? I feel like I am cheating here.