Swift Alamofire can not parse the response JSON string in POST request - swift

A POST http request:
task.request = sessionManager?.request(url!, method: method, parameters: paramters, encoding: JSONEncoding.prettyPrinted, headers: nil).responseJSON { response in
task.handleResponse(response: response)
}
The response is a JSON string like this:
{\"Data\":{\"ArrayOfItems\":[{\"ActualQty\":\"5.0\",\"BatchManaged\":true,\"ArrayOfBatches\":[{\"BatchNumber\":1,\"Counted\":\"ADD\", \"Quantity\":10},{\"BatchNumber\":2,\"Counted\":\"ADD\", \"Quantity\":10}],\"LineNum\":\"1\",\"ItemCode\":\"M1001-L\",\"WarehouseCode\":\"Store\"}],\"Comments\":\"Ht\",\"DocEntry\":\"1\",\"Quantiry\":\"123\",\"Initials\":\"RT\"},\"PromptAnswerValue\":[]}
The Alamofire can not parse the response JSON string. Errors is flonwing:
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error: Error Domain=NSCocoaErrorDomain Code=3840 "No string key for value in object around character 40." UserInfo={NSDebugDescription=No string key for value in object around character 40.}))
Debuged, the failed in 'let json = try JSONSerialization.jsonObject(with: validData, options: options)' line. See attached file flowing:
The validData is valuable. Why is this happening?
I write a demo and I can receving the response json string, the code like this:
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("\(jsonData.length)", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData as Data
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest) {(
data, response, error) in
guard let data = data, let _:URLResponse = response, error == nil else {
print("error")
return
}
let dataString = String(data: data, encoding: String.Encoding.utf8)
let dict = self.getDictionaryFromJSONString(jsonString: dataString!)
print(dict)
}
task.resume()
Used the URLSession can do this, but why the Alamofire can not? Is it the problem I use? Please advise.
Thanks.

The issues I had solved. This is caused by the invalid JSON string in response.
Debug as flowing:
Debug here and do this in the console
po String(data: validData, encoding: String.Encoding.utf8)
Check if the JSON string is valid.
Besides, note: Use JSONEncoding.prettyPrinted.

Related

Passing headers to URL with Swift URLSession

I don't feel like sharing the actual link as it may have some private information so don't be surprised if it doesn't work.
I have a link that looks like this: www.somelink.com/stuff/searchmembers?member=John
And some headers that I need to pass, like Login: Admin, Password: Admin
When I use this site everything seems to be working just fine, I put the link, make it GET and put headers in key:value format and as a result I get the list of all members, but how can I do the same with URLSession? Here's what I currently have and I don't get anything at all. What am I doing wrong there?
func getAllMembers(urlString: String) {
guard let url = URL(string: urlString) else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
request.httpBody = "member=John".data(using: .utf8)!
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(data)
}.resume()
}
Here is What You're Doing Wrong:
Your member=John is actually a URL query parameter. In general, URL requests have query parameters as a part of the URL string itself and not the httpbody.
Quick and Dirty Solution:
You should be good to go if you remove
request.httpBody = "member=John".data(using: .utf8)!
and instead pass the whole "www.somelink.com/stuff/searchmembers?member=John" into your getAllMembers(urlString:) function.
A Better Solution:
Let's say John's username is j o h n. Your function wouldn't make it past that first guard because spaces aren't valid URL string characters.
I like to use URLComponents because it saves me the trouble of having to handle spaces and such.
Here's how I'd write your function:
func getJohnMember(urlString: String) {
//URLComponents to the rescue!
var urlBuilder = URLComponents(string: urlString)
urlBuilder?.queryItems = [
URLQueryItem(name: "member", value: "j o h n")
]
guard let url = urlBuilder?.url else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(String(data: data, encoding: .utf8)) //Try this too!
}.resume()
}
Just to be clear, I would pass just "www.somelink.com/stuff/searchmembers" into the first parameter.
Now if I were to print(url) after the guard let, I'd get
www.somelink.com/stuff/searchmembers?member=j%20o%20h%20n
Much easier this way, no?
That member=John is a URL-query parameter, not part of the request body. So you need to add it to the URL itself.
func getAllMembers(urlString: String) {
guard let url = URL(string: "\(urlString)?member=John") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(data)
}.resume()
}

sending get / put / post in swift

I can easily issue a GET request and it returns (as expected) JSON data that is decoded to myDataModel object:
class func getData(completionHandler: #escaping (myDataModel?, Error?) -> Void)
{
let url = "https://example.com/api/someResource?ws_key=ABC...XYZ"
if let myUrl = URL(string: url)
{
URLSession.shared.dataTask(with: myUrl)
{
(data, response, err) in
if let data = data
{
do
{
let result = try JSONDecoder().decode(myDataModel.self, from: data)
completionHandler(result, nil)
}
catch let JSONerr
{
completionHandler(nil, JSONerr)
}
}
}.resume()
}
}
This work fine, so GET is no problem. (PS. the above has been simplified and modified.)
Likewise, I can issue a POST request and it returns (as expected) JSON data, when I use parameters like key1=value1&key2=value2. (I read that the default POST Content-Type is application/x-www-form-urlencoded.)
However, in another application I need to POST a piece of XML. After many tries and getting many errors, the approach I'm using is to: Set the header Content-Type to text/xml; charset=utf-8; Have no parameters and send the XML as the request body. I use a refined method:
PostHTTP(url: "https://example.com/api/someResource?ws_key=ABC...XYZ",
parameters: nil,
headers: ["Content-Type": "text/xml; charset=utf-8", "Content-Length": "\(xml.count)"],
body: "\(xml)") { (result) in ... }
(I image that you can determine what happens behind the scenes.)
For the POST request, to send a piece of XML:
Do I need to set the Content-Length or is this automatic?
Can I send parameters with the XML?
What headers (like Content-Type) do I require?
What structure (eg. xml=<?xml...) and encoding (eg. addingPercentEncoding) do I require?
Also I need to PUT data and I have similar method. The response from my attempt has the error
String could not be parsed as XML, XML length: 0
For a PUT request:
What headers (like Content-Type) do I require?
What structure (eg. xml=<?xml...) and encoding (eg. addingPercentEncoding) do I require?
Since I have tried many ways, an example of both PUT and POST would be ideal.
If you want to send data of XML you can do this in both PUT and POST
It does not have to be determined Content-Length
But you must add Content-Type
let req = NSMutableURLRequest(url: URL(string:"myUrl")!)
req.httpMethod = "POST"
req.setValue("application/xml;charset=utf-8;", forHTTPHeaderField: "Content-Type")
req.setValue("application/xml;", forHTTPHeaderField: "Accept")
var postBody = Data()
if let anEncoding = ("<?xml version='1.0' encoding='UTF-8'?>").data(using: .utf8) {
postBody.append(anEncoding)
}
if let anEncoding = "<Request>".data(using: .utf8) {
postBody.append(anEncoding)
}
if let anEncoding = "<test>\(123)</test>".data(using: .utf8) {
postBody.append(anEncoding)
}
if let anEncoding = "</Request>".data(using: .utf8) {
postBody.append(anEncoding)
}
req.httpBody = postBody
req.setValue("\(postBody.count)", forHTTPHeaderField: "Content-Length")
URLSession.shared.dataTask(with: req as URLRequest) { (data, urlreq, error) in
}

My swift HTTP POST request is not being processed properly by a PHP api

I am trying to get a post request to a PHP api. I need to be able to send the request in Json format. The PHP file collects the post request like so:
$postBody = $_POST ['request'];
$signature = $_POST ['signature'];
$rpcRequest = json_decode ( $postBody, true );
I need to build a request that is formatted so the api can read my information. I am building my request like so:
//Here I am building the request as a string. This string is used to get the signature.
var params =
"""
{"method":"phoneNumberVerificationStart","params":{"number":"\(PhoneNumber)"},"id":1}
"""
//here I build the request by using a dictionary.
var jsonParams = ["request": ["method": "phoneNumberVerificationStart","id": 1, "params": ["number": "\(PhoneNumber)"] ]] as NSMutableDictionary
let urlString = "******************************"
//below is just hashing the params into sha256
let hashedParams = sha256(request: params)
let signature = hashedParams.hexEncodedString()
//Take what was just hashed and put it into the signature variable
jsonParams["signature"] = signature
//jsonData takes my built dictionary and turns it into a json format to be sent.
let jsonData = try? JSONSerialization.data(withJSONObject: jsonParams, options: .prettyPrinted)
guard let requestURL = URL(string:urlString) else{return}
let session = URLSession.shared
// Set up the post request to send to the server.
let request = NSMutableURLRequest(url:requestURL)
request.httpMethod = "POST"
// Add the jsonData to the body of the http request. This data is json and when printed out in string form it looks like this:
// ( {"request":{"id":1,"method":"phoneNumberVerificationStart","params":{"number":"**********"}},"signature":"2ebdd87bdc66a04419bfd60e7c9b257039bf66dacd1623a1995c971e7cb68ed6"}
//For some odd reason Id shifts up to the front of the json file?
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
print(String(data: request.httpBody!, encoding: .utf8)!)
//After this I send the request the server does not understand my post request
let task = session.dataTask(with: request as URLRequest){
(data,respone, error) in
if error != nil {
print(error?.localizedDescription)
//print(String(data:myData!,encoding: .utf8)!)
}
do{
print (String(data: data!, encoding: .utf8)!)
}
I am thinking my problem is the request not being sent as a json object but rather raw data. I am receiving an error from the server that it cannot find the fields 'request' or 'signature'.

How to Call HTTP Get and Save in Variable In Swift?

My question is simple: how do you call a HTTP GET Request in Swift?
I am trying to retrieve specific data from server (I have the URL string), the problem is that the previous answers I saw, doesn't explain thoroughly how to request an HTTP Get and save the retrieved information in a variable to be used later? Thanks in advance!
Here's what I have so far:
let myURL = NSURL(string:"https://api.thingspeak.com/channels/CHANNEL_ID/last_entry
_id.txt");
let request = NSMutableURLRequest(url:myURL! as URL);
request.httpMethod = "GET"
Not sure what do following requesting the GET.
In your post you are missing the part that does the actual getting to of the data.
Your code should look something like this to get the value out of the text file.
var lastID: String?
let myURL = NSURL(string:"https://api.thingspeak.com/channels/1417/last_entry_id.txt");
let request = NSMutableURLRequest(url:myURL! as URL);
//request.httpMethod = "GET" // This line is not need
// Excute HTTP Request
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
// Check for error
if error != nil
{
print("error=\(error)")
return
}
// Print out response string
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("responseString = \(responseString!)")
lastID = "\(responseString!)" // Sets some variable or text field. Not that its unwrapped because its an optional.
}
task.resume()

Swift REST Request PUT Optional(502 Bad Gateway: Registered endpoint failed to handle the request.)

I'm trying to do a PUT request using Swift. In a REST client, when I try to do a REST request the following way:
In Body- x-www-form-urlencoded, I add vote=1 and with id being taken in the param for example: /user/:id, it works!
I try to do the same in Swift code, it does not work and I get responseString = Optional(502 Bad Gateway: Registered endpoint failed to handle the request.
Here is my code:
var baseURL = "http://<domain>/user"
let putURL = baseURL + "/\(id)"
print(putURL)
let request = NSMutableURLRequest(URL: NSURL(string: putURL)!)
request.HTTPMethod = "PUT"
let putString = "vote=1"
request.HTTPBody = putString.dataUsingEncoding(NSUTF8StringEncoding)
request.timeoutInterval = 1500
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
guard error == nil && data != nil else {
print("error=\(error)")
return
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
You forgot to set the content type, e.g.
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
Without that, the server won't know what to do with the blob of random data that you just sent it.
I'm not saying that this is necessarily the only problem, but it is definitely a problem, and one big enough to cause the error you're seeing.