Home > other >  SWIFT TASK CONTINUATION MISUSE: leaked its continuation
SWIFT TASK CONTINUATION MISUSE: leaked its continuation

Time:12-21

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.

  • Related