2

[NOTE: This has been edited for clarity since there seems to be some confusion about what I want. I only want the raw JSON. In this case, it should be {"foo":"bar"}. It is important to highlight that I want this as a string! I don't need it decoded into a Swift object or marshaled or modified in any way!]

Is it possible to get the raw JSON from an API call? Currently I'm using URLSession.dataTask(with:request) but the response data includes the response header along with the body. I only want the raw JSON as it comes back to me, as a string.

Here is the code I'm using:

// Build the URL
var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "127.0.0.1"
urlComponents.port = 80
urlComponents.queryItems = [URLQueryItem(name: "foo", value: "bar")]
guard let url = urlComponents.url else {
    fatalError("Could not create URL from components")
}

// Configure the request
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")

let task = URLSession.shared.dataTask(with: request) {(responseData, response, error) in
    DispatchQueue.main.async {
        guard error == nil else {
            print("ERROR: \(String(describing: error))")
            return
        }

        guard let _ = response else {
            print("Data was not retrieved from request")
            return
        }

        print("JSON String: \(String(describing: String(data: responseData!, encoding: .utf8)))")

        if let resData = responseData {
            let jsonResponse = try? JSONSerialization.jsonObject(with: resData, options: [])

            if let response = jsonResponse as? [String: Any] {
                print("RESPONSE:")
                dump(response)
            } else {
                print("SOMETHING WENT WRONG")
            }
        }
    }
}

task.resume()

This produces the following in the console:

JSON String: Optional("HTTP/1.1 OK\nContent-Type: application/json\n\n{\"foo\":\"bar\"}")
SOMETHING WENT WRONG
Darrell Brogdon
  • 6,843
  • 9
  • 47
  • 62

3 Answers3

12

You'll find the response body in the completion handler Data parameter. You decide what you want to do with it.

let task = session.dataTask(with: request) { (responseData, response, responseError) in
    guard responseData = responseData else { return }
    print("JSON String: \(String(data: responseData, encoding: .utf8))")
}
bbarnhart
  • 6,620
  • 1
  • 40
  • 60
4

Maybe I'm oversimplifying your question, but it reads like you're just wanting something as simple as data-to-string conversion.

let jsonString = String(data: responseData, encoding: .utf8)

christopherdrum
  • 1,513
  • 9
  • 25
2

You'll need to Serialize the response data to get the raw JSON:

URLSession.shared.dataTask(with: urlRequest) { (responseData, response, error) in

    // check for errors, then...

    if let resData = responseData {                    
        let jsonResponse = try? JSONSerialization.jsonObject(with: resData, options: [])

        if let response = jsonResponse as? [String: Any] {
             // ... handle response
        }
    }
}
JaredH
  • 2,338
  • 1
  • 30
  • 40
  • `dump(resData)` shows 177 bytes but `dump(jsonResponse)` returns `nil` – Darrell Brogdon May 15 '18 at 02:21
  • have you tried downcasting your serialized json like `try JSONSerialization.jsonObject(with: responseData, options: []) as? [[String: Any]]` – rmp May 15 '18 at 02:32
  • That gives me a "Cast from 'Data?' to unrelated type '[String : Any]' always fails" warning and "Cannot invoke 'jsonObject' with an argument list of type '(with: [String : Any], " error. – Darrell Brogdon May 15 '18 at 13:37
  • Check now @DarrellBrogdon – JaredH May 15 '18 at 13:41
  • `print(response)` doesn't produce anything but I do get `TIC Read Status [2:0x600000165c40]: 1:57` on the console. – Darrell Brogdon May 15 '18 at 13:43
  • `response` is a `Dictionary` type, `[String: Any]` @DarrellBrogdon - you have to look up your data by key to get the value. – JaredH May 15 '18 at 13:45
  • 1
    And `TIC Read Status [2:0x600000165c40]: 1:57` is what we call "Console spew" - ie: output that the developers of XCode use for their own diagnostic purposes. You can read more about that message [here](https://stackoverflow.com/a/46431730). – JaredH May 15 '18 at 13:49
  • I should have been clearer, my apologies. `if let response = jsonResponse as? [String: Any] {` appears to evaluate to false. If I put a `print()` statement in there, it doesn't execute. If I add an `else` to the `if` statement, with a `print` inside the else, then that is the one that gets executed. – Darrell Brogdon May 15 '18 at 14:26
  • Sounds like there's an issue deserializing your JSON. Are you sure it's valid JSON being sent? – JaredH May 15 '18 at 14:28
  • This is the raw data coming back: `"HTTP/1.1 OK\nContent-Type: application/json\n\n{\"foo\":\"bar\"}"` Unless something internal is striping out the header, then that is what is getting included in the serialization. But that's my whole point; I *only* want the JSON as a *string*. I don't need it to be an object and I don't want the header! If there is a way of taking that raw data and stripping out the header, then that's fine. – Darrell Brogdon May 15 '18 at 15:04