I am using URLSession.shared.dataTask(with:completionHandler:)
to access an API which, on certain circumstances, won't respond immediately. I also need to cancel pending requests on certain conditions.
My current implementation uses the completionHandler
approach and multiple requests can happen in parallel. This makes it very inconvenient to change the pattern to a delegate.
What I am trying is to store every pending task in a set, and then remove the task from the set once they are done. The problem is accessing the return of the dataTask from inside the completion handler seems very cumbersome, to a point I am not sure this is even correct.
class API {
private var tasks: Set<URLSessionTask> = []
func sendRequest(path: String, body: [String: Any], completionHandler: @escaping @Sendable (APIResponse) -> Void) {
var request = // ... create request
let task = URLSession.shared.dataTask(with: request) { [self] data, response, error in
// ... handle response
tasks.remove(task)
}
tasks.insert(task)
task.resume()
}
func cancelAllTasks() {
for task in tasks {
task.cancel()
tasks.remove(task)
}
}
}
I either get a compiler error Closure captures 'task' before it is declared
or a warning 'task' mutated after capture by sendable closure
.
Is there a better way of doing this?
CodePudding user response:
The problem is that your completion handler tries to reference the task which isn't yet defined when you create the completion handler.
Assuming your requests are unique, you could use a dictionary of your data tasks keyed by their URLRequest. Then add a method taskComplete(forRequest:)
to your API class. Have your completion handler send that message to the API class.
The API class could remove the task using the request as a key (I checked. URLRequest
is Hashable
.
If your requests aren't certain to be unique, create a request struct that contains the URLTask and the Date when the task is created. The Date should make the hash of the request struct unique.