swifthttpget

Making HTTP GET request with Swift 5


I am obviously missing something very fundamental/naïve/etc., but for the life of me I cannot figure out how to make simple GET requests.

I'm trying to make an HTTP GET request with Swift 5. I've looked at these posts/articles: one, two, but I can't get print() statements to show anything. When I use breakpoints to debug, the entire section within the URLSession.shared.dataTask section is skipped.
I am looking at the following code (from the first link, above):

func HTTP_Request() {
    let url = URL(string: "http://www.stackoverflow.com")!

    let task = URLSession.shared.dataTask(with: url) {(data: Data?, response: URLResponse?, error: Error?) in
        guard let data = data else { return }
        print(String(data: data, encoding: .utf8)!)
    }
    task.resume()
}

HTTP_Request()

I am running this in a MacOS Command Line Project created through XCode.

I would greatly appreciate any help I can get on this, thank you.


Solution

  • Right now, if there is an error, you are going to silently fail. So add some error logging, e.g.,

    func httpRequest() {
        let url = URL(string: "https://www.stackoverflow.com")! // note, https, not http
    
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard
                error == nil,
                let data = data,
                let string = String(data: data, encoding: .utf8)
            else {
                print(error ?? "Unknown error")
                return
            }
    
            print(string)
        }
        task.resume()
    }
    

    That should at least give you some indication of the problem.

    A few other considerations:

    1. If command line app, you have to recognize that the app may quit before this asynchronous network request finishes. One would generally start up a RunLoop, looping with run(mode:before:) until the network request finishes, as advised in the run documentation.

      For example, you might give that routine a completion handler that will be called on the main thread when it is done. Then you can use that:

      func httpRequest(completion: @escaping () -> Void) {
          let url = URL(string: "https://www.stackoverflow.com")! // note, https, not http
      
          let task = URLSession.shared.dataTask(with: url) { data, response, error in
              defer {
                  DispatchQueue.main.async {
                      completion()
                  }
              }
      
              guard
                  error == nil,
                  let data = data,
                  let string = String(data: data, encoding: .utf8)
              else {
                  print(error ?? "Unknown error")
                  return
              }
      
              print(string)
          }
          task.resume()
      }
      
      var finished = false
      
      httpRequest {
          finished = true
      }
      
      while !finished {
          RunLoop.current.run(mode: .default, before: .distantFuture)
      }
      
    2. In standard macOS apps, you have to enable outgoing (client) connections in the “App Sandbox” capabilities.

    3. If playground, you have to set needsIndefiniteExecution.

    4. By default, macOS and iOS apps disable http requests unless you enable "Allow Arbitrary Loads” in your Info.plist. That is not applicable to command line apps, but you should be aware of that should you try to do this in standard macOS/iOS apps.

      In this case, you should just use https and avoid that consideration altogether.