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
request
to the closure’s capture list, but that’s not possible in this case. E.g., you could create aTask
yourself with a capture list, achieving something akin toasync let
, but with greater control:Obviously, you can simply
await
thedata(for:delegate:)
, rather thanasync let
, and the problem goes away: