Home > Net >  Function works in playground but not in XCode test
Function works in playground but not in XCode test

Time:11-27

I have a function that fetches data from a URL:

private func fetch(url: URL) -> String? {
    var htmlString: String?
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        guard error == nil, let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            return
        }
        if let mimeType = httpResponse.mimeType, mimeType == "text/html",
           let data = data,
           let string = String(data: data, encoding: .utf8) {
            htmlString = string
        }
    }
    task.resume()
    
    return htmlString
}

This function works in a playground, when I test it as part of a package in XCode, it doesn't seem to run past the let task... statement. How do I fix this?

CodePudding user response:

The dataTask rendition that returns the result immediately would only work if your device happened to have a cached response ready to deliver. Otherwise, it would return before the dataTask had a chance to complete.

One needs to wait for the response from the server before returning the result. In Swift concurrency, we would await:

enum WebError: Error {
    case notSuccess(Int)
    case notHtml
}

private func fetch(url: URL) async throws -> String? {
    let (data, response) = try await URLSession.shared.data(from: url)
    
    guard let response = response as? HTTPURLResponse else {
        throw URLError(.badServerResponse)
    }
    
    guard 200...299 ~= response.statusCode else {
        throw WebError.notSuccess(response.statusCode)
    }
    
    guard
        response.mimeType == "text/html",
        let string = String(data: data, encoding: .utf8) 
    else {
        throw WebError.notHtml
    }

    return string
}

Note, I also throw errors, so that if something goes wrong, the caller can catch the error and successfully diagnose what went wrong.


See WWDC 2021 video Use async/await with URLSession.

  • Related