0

I want to make a generic HTTP request function. The code I saw does not return data to the caller. Instead it prints out the error code or the parsed JSON object within the function. In my case I would like to return (data, response, error) to the caller.

func performHTTPRequest(urlString: String) -> (Data, URLResponse, Error) {
    
    
   if let url = URL(string: urlString) {
       
       let session = URLSession(configuration: .default)
       
       let task = session.dataTask(with: url) {(data, response, error) in

       // some logic

       }

       task.resume()

   }

}

The problem is the three variables (data, response, error) are not available outside the closure. If I assign them to global variables within the closure, compiler complains the global variables are not in scope.

Also, where would I put the return (data, response, error) statement? Before or after task.resume()? Thanks

1
  • You want to use the async version of URLSession's data task. Commented Jan 13, 2022 at 18:55

2 Answers 2

2

For example you can do that

struct Message: Decodable {
  var username: String
  var message: String
}

enum RequestError: Error {
  case invalidURL
  case missingData
}


func performHTTPRequest(urlString: String) async throws -> Message{

  guard let url = URL(string: urlString) else {throw RequestError.invalidURL}
  guard let (data, response) = try? await URLSession.shared.data(from: url) else{throw RequestError.invalidURL}
  guard (response as? HTTPURLResponse)?.statusCode == 200 else {throw RequestError.invalidURL}
  let decoder = JSONDecoder()
  guard let jsonResponse = try? decoder.decode(Message.self, from: data) else {throw RequestError.missingData}
  return jsonResponse
   
}

and call the fonction

do {
    try await performHTTPRequest(urlString: "wwww.url.com")
} catch RequestError.invalidURL{
    print("invalid URL")
} catch RequestError.missingData{
    print("missing data")
}
Sign up to request clarification or add additional context in comments.

Comments

1

The short answer is, you can't. You can us the new async/await syntax in Swift 5.5 to simulate a synchronous network call. (I haven't had a chance to use async/await in my own projects yet, so I'd have to look that up in order to guide you.)

Without async/await, you will need to refactor your function to take a completion handler. You'd then call the completion handler with the results once the data task completes.

This question comes up all the time on SO. You should be able to find dozens of examples of writing a completion handler-based function for async networking.

1 Comment

Thx. Even with a completion handler, returned json object only exists within the context of the session. Can't be copied to global variables within the handler method. It seems I have to use the delegate/protocol design pattern to pass data to caller. Is this correct?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.