I want to create a URL request and pass it into an async let binding, which seems natural to me:
func test() async {
// Force unwraps (!) are just for demo
var request = URLRequest(url: URL(string:"https://stackoverflow.com")!)
request.httpMethod = "GET" // just for example
// some more tinkering with `request` here.
// Error on this line: "Reference to captured var 'request' in concurrently-executing code"
async let responseData = URLSession.shared.data(for: request).0
// It works like this:
// let immutableRequest = request
// async let responseData = URLSession.shared.data(for: immutableRequest).0
// other stuff
print("Response body: \(String(data: try! await responseData, encoding: .utf8))")
}
Why do I get an error? URLRequest is a struct, so when we pass it into a function, the function should get a copy of that struct, so if I modify request after the async call, it shouldn't affect the call.
I know that the call happens asynchronously, but I would expect it to capture the parameters at the point of the call and then continue execution as though the call has been made (so, a copy of request at the point of the call has been passed into data(for: request).
Also, is there a convenient way to do it without creating another let variable and without using a closure to initialize request, like:
let request: URLRequest = {
var result = URLRequest(url: URL(string:"https://stackoverflow.com")!)
result.httpMethod = "GET"
return result
}()
As SE-0317 - async let bindings says:
So, it is not the case that the parameter to
data(for:delegate:)is copied and then the asynchronous task is created, but rather the other way around.Usually, if you were using a closure, you would just add
requestto the closure’s capture list, but that’s not possible in this case. E.g., you could create aTaskyourself with a capture list, achieving something akin toasync let, but with greater control:Obviously, you can simply
awaitthedata(for:delegate:), rather thanasync let, and the problem goes away: