How to get readable JSON after NSJSONSerialization.dataWithJSONObject in Swift - ios

I have some code similar to this (I've simplified it here):
let text = "abc" let iosVersion = UIDevice.currentDevice().systemVersion
let message = ["Text" : text, "IosVersion" : iosVersion]
if NSJSONSerialization.isValidJSONObject(message){
let url = NSURL(string: "http://localhost:3000/api/someapi")
var request = NSMutableURLRequest(URL: url!)
var data = NSJSONSerialization.dataWithJSONObject(message, options: nil, error: nil)
print(data)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
request.HTTPBody = data
let task = session.dataTaskWithRequest(request, completionHandler: nil)
task.resume()
}
This works fine, but I'd like to see the JSON in a readable format so that I can copy/paste it into fiddler/curl to help diagnose my API at the server end. The println(data) line above gives me hexadecimal data. Any ideas?

Create a String from Data and it's good practice to handle the error
do {
let data = try JSONSerialization.data(withJSONObject: message)
let dataString = String(data: data, encoding: .utf8)!
print(dataString)
// do other stuff on success
} catch {
print("JSON serialization failed: ", error)
}

Related

Issue in parsing Json Response in iOS application, getting "=" at the place of ":" in Dictionary

I am working on an iOS application and using swift in it. I am calling an Rest api and response is JSON.
Here is my code:
{
let request = NSMutableURLRequest(url: NSURL(string: path)! as URL)
// Set the method to POST
request.httpMethod = "POST"
do {
// Set the POST body for the request
let jsonBody = try JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
request.httpBody = jsonBody
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// request.addValue("Cookie", forHTTPHeaderField: session_Id)
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let jsonData = data {
print("\(data?.debugDescription)")
do {
print("JSON Response String: \(String.init(data: data!, encoding: .utf8))")
let dict:[String:Any] = (try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String:Any])!
print("JSON Response Dictionary: \(dict)")
onCompletion(dict, nil)
} catch {
// report ERROR
print("caught: \(error)")
onCompletion(nil, error as! NSError)
}
} else {
print(error)
onCompletion(nil, error as! NSError)
}
})
task.resume()
} catch {
// Create your personal error
onCompletion(nil, nil)
}
}
And This is the response of api:
======== - Fetch CC list api request - =============
["userID": "898465844"]
======== - Fetch CC list api request - =============
JSON Response String: "{\"status\":\"success\",\"card_list\":[{\"cardType\":\"Visa\",\"cardholderName\":null,\"expirationMonth\":\"01\",\"expirationYear\":\"2020\",\"cardImage\":\"https://assets.braintreegateway.com/payment_method_logo/visa.png?environment=sandbox\",\"cardNumber\":\"411111******1111\",\"token\":\"348nws\"}]}"
JSON Response Dictionary: ["status": success, "card_list": <__NSSingleObjectArrayI 0x1c060f350>(
{
cardImage = "https://assets.braintreegateway.com/payment_method_logo/visa.png?environment=sandbox";
cardNumber = "411111******1111";
cardType = Visa;
cardholderName = "<null>";
expirationMonth = 01;
expirationYear = 2020;
token = 348nws;
}
)
]
After parsing I am getting "=" at the place of ":" in "card_list" array of dictionary.
So I am not able to figure out why I am getting "=" at the place of ":".
The format of your response is not look like JSON. Its property list or (XML) try to use - PropertyListSerialization.propertyList(from:...) or some XML parsers

Swift HTTP Post Request returns HTML of site instead of JSON response

I am trying to reach a site that should take the username and password given and return a JSON which contains information stating whether or not the login data provided was valid or not.
However, all I'm getting back is the site's HTML code instead of a response. I've tried the request with the same parameters on https://www.hurl.it/ and have gotten the correct response so that does not seem to be the issue.
I use the following code:
private func uploadToAPI(username: String, password: String) {
guard let url = URL(string: "http://api.foo.com/login.php"),
let encodedUsername = username.addingPercentEncoding(withAllowedCharacters: CharacterSet.alphanumerics),
let encodedPassword = password.addingPercentEncoding(withAllowedCharacters: CharacterSet.alphanumerics) else {
self.loginButton.isLoading = false
return
}
let httpBodyParameters = ["user": encodedUsername, "password": encodedPassword, "client": "ios", "version": "5"]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: httpBodyParameters, options: JSONSerialization.WritingOptions.prettyPrinted)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response.mimeType) // Prints "text/html"
}
if let data = data {
print(try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)) // Prints nil
print(String(data: data, encoding: String.Encoding.utf8)) // Prints the site's HTML
}
}.resume()
}
I fail to see where the issue is. I've also tried not setting the HTTP headers but that makes no difference. Anyone got any ideas?
It seems like not setting the HTTP header fields and using a string literal instead of a Dictionary as HTTP body data did it for me.
For anyone interested this is the code that now receives the expected response:
guard let url = URL(string: "http://api.foo.com/login.php?"),
let encodedUsername = username.addingPercentEncoding(withAllowedCharacters: CharacterSet.alphanumerics),
let encodedPassword = password.addingPercentEncoding(withAllowedCharacters: CharacterSet.alphanumerics) else {
if let delegate = self.delegate {
delegate.viewModelDidRejectLogin(self)
}
return
}
let httpBodyString = "user=\(encodedUsername)&password=\(encodedPassword)&client=ios&version=5"
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = httpBodyString.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print(error)
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String : AnyObject] {
self.readLoginResponse(json)
}
} catch {
print(error)
}
}.resume()

Why format is strange when JSON is parsed in Swift3?

I have a struggle in parsing JSON data using Oxford Dictionary API. I can retrieve JSON data and at this point, there is no problem. But the format is a bit strange. In the example of JSON data by OXford Document, it looks like;
"results": [
{
"id": "string",
"language": "string",
"lexicalEntries": [
{
"entries": [
{..........
Unlike the example above, the array in my JSON is not surrouned by [], but ()
Why is this happening??
Updated
I used Alamofire to produce it. The code is;
func getJSONData() {
let appId = ""
let appKey = ""
let language = "en"
let word = "play"
let word_id = word.lowercased()
let url = URL(string: "https://od-api.oxforddictionaries.com/api/v1/entries/\(language)/\(word_id)")!
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue(appId, forHTTPHeaderField: "app_id")
request.addValue(appKey, forHTTPHeaderField: "app_key")
Alamofire.request(request).responseJSON { response in
print("Request \(response.request)") // original URL request
print("Response \(response.response)") // HTTP URL response
print("Data \(response.data)") // server data
print("Result \(response.result)") // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
}
I followed all code in Oxford Dictionary Document, but the printed out JSON was far from the JSON in the document. So I tried to use Alamofire in the hope for it to work correctly, but the result was the same. https://developer.oxforddictionaries.com/documentation
Update2
Okay, I understood why this was an error. To people who might have the same problem, I post the answer for the problem I had.
enum JSONError: String, Error {
case NoData = "ERROR: no data"
case ConversionFailed = "ERROR: conversion from JSON failed"
}
func jsonParser() {
let appId = ""
let appKey = ""
let language = "en"
let word = "Ace"
let word_id = word.lowercased() //word id is case sensitive and lowercase is required
let url = URL(string: "https://od-api.oxforddictionaries.com:443/api/v1/entries/\(language)/\(word_id)")!
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue(appId, forHTTPHeaderField: "app_id")
request.addValue(appKey, forHTTPHeaderField: "app_key")
URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
guard let data = data else {
throw JSONError.NoData
}
guard let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary else {
throw JSONError.ConversionFailed
}
if let stringJSON = String(data: data, encoding: String.Encoding.utf8) {
print(stringJSON)
}
print(json)
} catch let error as JSONError {
print(error.rawValue)
} catch let error as NSError {
print(error.debugDescription)
}
}.resume()
}
Strange printed JSON format This post helped me. This bugged me for a while. Thank you very much #EricAya

Swift: Save responseString ID to String

Sending this code with HTTP POST returns an ID, but I'm so far unable to extract the ID from responseString and save it as it's own String in my app.
I'm looking into using Alamofire, perhaps that'll make things easier but I was hoping to be able to do it using just Swift code. Any help is appreciated.
var parseError: NSError?
let date = NSDate()
let timeStamp = date.timeIntervalSince1970
var request = NSMutableURLRequest(URL: NSURL(string: URL)!)
request.HTTPMethod = "POST"
let params = ["name":fullName.text, "number":phoneNumber.text, "email":emailAddress.text, "timeStarted":timeStamp] as Dictionary<String, AnyObject>
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: nil)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
println("response = \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("responseString = \(responseString)")
let idFromServer = responseString?.valueForKeyPath("id") as String!
println(idFromServer)
var dateID = idFromServer
newUser.setValue(dateID, forKey: "dateID")

HTTP Request in Swift with POST method

I'm trying to run a HTTP Request in Swift, to POST 2 parameters to a URL.
Example:
Link: www.thisismylink.com/postName.php
Params:
id = 13
name = Jack
What is the simplest way to do that?
I don't even want to read the response. I just want to send that to perform changes on my database through a PHP file.
The key is that you want to:
set the httpMethod to POST;
optionally, set the Content-Type header, to specify how the request body was encoded, in case server might accept different types of requests;
optionally, set the Accept header, to request how the response body should be encoded, in case the server might generate different types of responses; and
set the httpBody to be properly encoded for the specific Content-Type; e.g. if application/x-www-form-urlencoded request, we need to percent-encode the body of the request.
E.g., in Swift 3 and later you can:
let url = URL(string: "https://httpbin.org/post")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.httpMethod = "POST"
let parameters: [String: Any] = [
"id": 13,
"name": "Jack & Jill"
]
request.httpBody = parameters.percentEncoded()
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard
let data = data,
let response = response as? HTTPURLResponse,
error == nil
else { // check for fundamental networking error
print("error", error ?? URLError(.badServerResponse))
return
}
guard (200 ... 299) ~= response.statusCode else { // check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
// do whatever you want with the `data`, e.g.:
do {
let responseObject = try JSONDecoder().decode(ResponseObject<Foo>.self, from: data)
print(responseObject)
} catch {
print(error) // parsing error
if let responseString = String(data: data, encoding: .utf8) {
print("responseString = \(responseString)")
} else {
print("unable to parse response as string")
}
}
}
task.resume()
Where the following extensions facilitate the percent-encoding request body, converting a Swift Dictionary to a application/x-www-form-urlencoded formatted Data:
extension Dictionary {
func percentEncoded() -> Data? {
map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed: CharacterSet = .urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
And the following Decodable model objects facilitate the parsing of the application/json response using JSONDecoder:
// sample Decodable objects for https://httpbin.org
struct ResponseObject<T: Decodable>: Decodable {
let form: T // often the top level key is `data`, but in the case of https://httpbin.org, it echos the submission under the key `form`
}
struct Foo: Decodable {
let id: String
let name: String
}
This checks for both fundamental networking errors as well as high-level HTTP errors. This also properly percent escapes the parameters of the query.
Note, I used a name of Jack & Jill, to illustrate the proper x-www-form-urlencoded result of name=Jack%20%26%20Jill, which is “percent encoded” (i.e. the space is replaced with %20 and the & in the value is replaced with %26).
See previous revision of this answer for Swift 2 rendition.
Swift 4 and above
func postRequest() {
// declare the parameter as a dictionary that contains string as key and value combination. considering inputs are valid
let parameters: [String: Any] = ["id": 13, "name": "jack"]
// create the url with URL
let url = URL(string: "www.thisismylink.com/postName.php")! // change server url accordingly
// create the session object
let session = URLSession.shared
// now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
// add headers for the request
request.addValue("application/json", forHTTPHeaderField: "Content-Type") // change as per server requirements
request.addValue("application/json", forHTTPHeaderField: "Accept")
do {
// convert parameters to Data and assign dictionary to httpBody of request
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
return
}
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
print("Post Request Error: \(error.localizedDescription)")
return
}
// ensure there is valid response code returned from this HTTP response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode)
else {
print("Invalid Response received from the server")
return
}
// ensure there is data returned
guard let responseData = data else {
print("nil Data received from the server")
return
}
do {
// create json object from data or use JSONDecoder to convert to Model stuct
if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
print(jsonResponse)
// handle json response
} else {
print("data maybe corrupted or in wrong format")
throw URLError(.badServerResponse)
}
} catch let error {
print(error.localizedDescription)
}
}
// perform the task
task.resume()
}
For anyone looking for a clean way to encode a POST request in Swift 5.
You don’t need to deal with manually adding percent encoding.
Use URLComponents to create a GET request URL. Then use query property of that URL to get properly percent escaped query string.
let url = URL(string: "https://example.com")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
components.queryItems = [
URLQueryItem(name: "key1", value: "NeedToEscape=And&"),
URLQueryItem(name: "key2", value: "vålüé")
]
let query = components.url!.query
The query will be a properly escaped string:
key1=NeedToEscape%3DAnd%26&key2=v%C3%A5l%C3%BC%C3%A9
Now you can create a request and use the query as HTTPBody:
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = Data(query.utf8)
Now you can send the request.
Heres the method I used in my logging library: https://github.com/goktugyil/QorumLogs
This method fills html forms inside Google Forms.
var url = NSURL(string: urlstring)
var request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.HTTPBody = postData.dataUsingEncoding(NSUTF8StringEncoding)
var connection = NSURLConnection(request: request, delegate: nil, startImmediately: true)
let session = URLSession.shared
let url = "http://...."
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
var params :[String: Any]?
params = ["Some_ID" : "111", "REQUEST" : "SOME_API_NAME"]
do{
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: JSONSerialization.WritingOptions())
let task = session.dataTask(with: request as URLRequest as URLRequest, completionHandler: {(data, response, error) in
if let response = response {
let nsHTTPResponse = response as! HTTPURLResponse
let statusCode = nsHTTPResponse.statusCode
print ("status code = \(statusCode)")
}
if let error = error {
print ("\(error)")
}
if let data = data {
do{
let jsonResponse = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions())
print ("data = \(jsonResponse)")
}catch _ {
print ("OOps not good JSON formatted response")
}
}
})
task.resume()
}catch _ {
print ("Oops something happened buddy")
}
All the answers here use JSON objects. This gave us problems with the
$this->input->post()
methods of our Codeigniter controllers. The CI_Controller cannot read JSON directly.
We used this method to do it WITHOUT JSON
func postRequest() {
// Create url object
guard let url = URL(string: yourURL) else {return}
// Create the session object
let session = URLSession.shared
// Create the URLRequest object using the url object
var request = URLRequest(url: url)
// Set the request method. Important Do not set any other headers, like Content-Type
request.httpMethod = "POST" //set http method as POST
// Set parameters here. Replace with your own.
let postData = "param1_id=param1_value&param2_id=param2_value".data(using: .utf8)
request.httpBody = postData
// Create a task using the session object, to run and return completion handler
let webTask = session.dataTask(with: request, completionHandler: {data, response, error in
guard error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
guard let serverData = data else {
print("server data error")
return
}
do {
if let requestJson = try JSONSerialization.jsonObject(with: serverData, options: .mutableContainers) as? [String: Any]{
print("Response: \(requestJson)")
}
} catch let responseError {
print("Serialisation in error in creating response body: \(responseError.localizedDescription)")
let message = String(bytes: serverData, encoding: .ascii)
print(message as Any)
}
// Run the task
webTask.resume()
}
Now your CI_Controller will be able to get param1 and param2 using $this->input->post('param1') and $this->input->post('param2')
#IBAction func btn_LogIn(sender: AnyObject) {
let request = NSMutableURLRequest(URL: NSURL(string: "http://demo.hackerkernel.com/ios_api/login.php")!)
request.HTTPMethod = "POST"
let postString = "email: test#test.com & password: testtest"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){data, response, error in
guard error == nil && data != nil else{
print("error")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200{
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = String(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
}

Resources