I am struggling to understand why my code doesn't work in Swift6 mode.
Here is a minimalistic example:
final actor ODRRequest {
let request = NSBundleResourceRequest(tags: ["bla"])
func startRequest() {
Task {[unowned self] in
let available = await request.conditionallyBeginAccessingResources()
if available {
updateState(.available)
}
}
}
}
This fails telling me that "Sending actor-isolated value of type 'NSBundleResourceRequest' with later accesses to nonisolated context risks causing data races".
At the same time, this works without issues:
private func startRequest() {
request.conditionallyBeginAccessingResources {[unowned self] available in
Task {
if available {
await updateState(.available)
}
}
}
}
Can anyone explain to me why does compiler complain about the first implementation but does not complain about the second? These functions are supposed to be identical.
I think you can do what you're trying to do — i.e., move your request and the methods that talk to it off into a single instance — by using a simple class
instead of an actor
; here's a sketch:
final class ODRRequest {
var request: NSBundleResourceRequest?
func startRequest() async {
let request = NSBundleResourceRequest(tags: ["bla"])
if await request.conditionallyBeginAccessingResources() {
// do stuff
}
self.request = request
}
func beginAccessing() async throws {
try await request?.beginAccessingResources()
/// do stuff if we get here
}
func stopAccessing() {
request?.endAccessingResources()
request = nil
}
}
That compiles on my machine and I don't think I'm cheating the compiler in some way. Note the use of an Optional; that's how I always do bundle resource requests, so that I can create the request when I'm ready to start using it, and release the request when I'm finished with it. (Either that or I hold my requests in a mutable dictionary, keyed by tags, but it amounts to the same thing.)