Andriy Gordiychuk
Andriy Gordiychuk

Reputation: 6272

Swift6 concurrency and NSBundleResourceRequest

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.

Upvotes: 0

Views: 135

Answers (1)

matt
matt

Reputation: 535889

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.)

Upvotes: 1

Related Questions