I'm using the following function to do a fetch request using Alamofire:
func afRequest(url: URL) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
// timeoutInterval is in seconds
AF.request(url, method: .get, requestModifier: { $0.timeoutInterval = 10 }).validate(statusCode: 200..<600).responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
guard case let .failure(error) = response.result else { return }
switch error {
case .invalidURL(let url):
print("Invalid URL: \(url) - \(error.localizedDescription)")
continuation.resume(throwing: error)
}
}
}
}
I intentionally set the timeout to 10 seconds to test this out. So the error is "The request timed out."
. After that I get this error:
SWIFT TASK CONTINUATION MISUSE: afRequest(url:) leaked its continuation!
I was looking at this post, but it's not clear what they accomplished or the solution. What is the proper way to mitigate this leak?
Edit:
This is how I switched the above part, but still says the same:
if case let .failure(error) = response.result {
switch error {
case .invalidURL(let url):
...
continuation.resume(throwing: error)
return
Edit 2 - here's the new handling of the error, but still says the same:
func afRequest(url: URL) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
// timeoutInterval is in seconds
AF.request(url, method: .get, requestModifier: { $0.timeoutInterval = .infinity }).validate(statusCode: 200..<600).responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
if case let .failure(error) = response.result {
switch error {
case .invalidURL(let url):
print("Invalid URL: \(url) - \(error.localizedDescription)")
// There are more cases...
continuation.resume(throwing: error)
return
}
}
else {
continuation.resume(throwing: Error.self as! Error)
return
}
}
}
}
CodePudding user response:
Your guard is capturing a case and not calling the continuation. You need to call the continuation (e.g. continuation.resume(...)
) from all branches so that it is not leaked.
This early return
is causing the issue
guard case let .failure(error) = response.result else { return }
In your example you would need to decide how to handle response.data
being nil and response.result
being success and then update your guard
to invoke continuation.resume(...)
appropriately.