HTTP POST request with String and NSDate Swift - swift

i'm have this parameters on payload to send a POST request to backend
'{"token":"xxxx", "extra_information": {"expires_in": xxxx, "refresh_token": "xxx", "user_id": "user_uuid", "token_type": "Bearer"}}'
Parameters with "xxxx" will be come by integration. I'm try create a function to send this
func sendAuth() {
if let url = NSURL(string: "https://xxxxxxxxxxxx"){
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let token = AccessToken?()
let params = ["token" : (token?.tokenString)!, "refresh_token" : (token?.refreshToken)!,"expires_in" : (token?.expirationDate)!, "user_id" : "uber_uuid" , "token_type" : "Bearer"] as Dictionary <String,String>
let httpData = NSKeyedArchiver.archivedDataWithRootObject(params)
request.HTTPBody = httpData
let session = ServicesUtils.BaseSessionManager()
session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
print("\(strData)")
}).resume()
After write this xCode show me a error "Cannot convert Value of Type NSDate to expected dictionary value Type String" due to parameter expires_in receive a NSDate.
Edit 1 - After change Dictionay receive strData occured error "Cannot convert value of type 'NSURLResponse to expected argument type 'NSData'"

Its because you are casting params as Dictionary <String,String> thats why it will not accept NSDate as value in your dictionary.
You need to change it like Dictionary <String,AnyObject> and it will work.

Related

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

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.

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'.

Alamofire Custom Parameter Encoding

I'm using Alamofire and want to encode my parameters with content type "text/html; charset=utf-8". I followed the documentation https://github.com/Alamofire/Alamofire for custom parameter encoding and created
let custom: (URLRequestConvertible, parameters: [String: AnyObject]?) -> (NSURLRequest, NSError?) = {
(URLRequest, parameters) in
let mutableURLRequest = URLRequest.URLRequest.mutableCopy() as! NSMutableURLRequest
mutableURLRequest.setValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type")
mutableURLRequest.body = // don't know if I need to set this
return (mutableURLRequest, nil)
}
func postData(){
Alamofire.request(.POST, baseUrl, parameters: parameters, encoding: .Custom(custom))
.responseString{ (request, response, data, error) in
println("blah")
}
}
I have a problem when I try to use custom in my Alamofire request and get the error "Cannot make responseString with argument list of type ( _, _, _, _)-> _ )" However, this isn't a problem if the encoding is changed to a preset like .URL so the issue seems to be in my implementation of custom?
If it makes a difference my parameters are set here:
var parameters = [String: AnyObject]()
func setParams(){
parameters = [
"CONTRACT ID" : chosenContract!.iD.toInt()!,
"FEE AMOUNT" : 0,
"TRANSACT DATE" : today
]
}
You have a couple questions in here. Let's break them down 1x1.
Compiler Issue
Your compiler issue is due to the fact that your return tuple is the wrong type. In Alamofire 1.3.0, we changed the return type to be an NSMutableURLRequest which ends up making things much easier overall. That should fix your compiler issue.
Setting the HTTPBody
Now you have a couple options here.
Option 1 - Encode Data as JSON
let options = NSJSONWritingOptions()
let data = try NSJSONSerialization.dataWithJSONObject(parameters!, options: options)
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.HTTPBody = data
From what you posted I'm assuming you actually want .URL encoding for the parameters.
Option 2 - Use the .URL Case
let parameters: [String: AnyObject] = [:] // fill in
let encodableURLRequest = NSURLRequest(URL: URL)
let encodedURLRequest = ParameterEncoding.URL.encode(encodableURLRequest, parameters).0
let mutableURLRequest = NSMutableURLRequest(URL: encodedURLRequest.URLString)
mutableURLRequest.HTTPMethod = "POST"
mutableURLRequest.setValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type")
Alamofire.request(mutableURLRequest)
.response { request, response, data, error in
print(request)
print(response)
print(error)
}
Hopefully that helps get you going. Best of luck!

How do I upload a file attachment using POST?

I've created an audio file I want to attach to an "Order" object in Salesforce. The path to the file is properly located at theSoundPath.
The following code causes an error that reads: 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (NSConcreteData)'
I'm encoding the file in base64 as required by the API. But I'm also thinking that's what's throwing this error. Would appreciate if someone could point me in the right direction.
Here's the relevant code (using SwiftyJSON and Alamofire):
let encodedSound = NSFileManager.defaultManager().contentsAtPath(self.theSoundPath)
let encodedBase64Sound = encodedSound!.base64EncodedDataWithOptions(nil)
let tokenParam = "Bearer " + theToken // Bearer prefix required by API
let theWriteURL = theInstance + "/services/data/v33.0/sobjects/Order/" + self.theOrder + "/Attachment/"
let URL = NSURL(string: theWriteURL)!
let mutableURLRequest = NSMutableURLRequest(URL: URL)
mutableURLRequest.HTTPMethod = "POST"
let parameters = [
"AccountId": "001xxxxxxxxxxxx",
"Name": "testfile.aac",
"Body": encodedBase64Sound
]
var JSONSerializationError: NSError? = nil
mutableURLRequest.HTTPBody = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: &JSONSerializationError)
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.setValue(tokenParam, forHTTPHeaderField: "Authorization")
Alamofire.request(mutableURLRequest)
.responseJSON { (req, res, json, error) in // . . . etc.
From NSJSONSerialization's doc
An object that may be converted to JSON must have the following
properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray,
NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
But you try to serialize a NSData object, the result of
encodedBase64Sound = encodedSound!.base64EncodedDataWithOptions(nil)
Instead create a base64 string
encodedBase64Sound = encodedSound!.base64EncodedStringWithOptions(nil)

Converting CURL command for Locu to Swift

The Locu API provides this example using CURL to perform a location sensitive query:
curl -X POST https://api.locu.com/v2/venue/search -d '{
"api_key" : "f165c0e560d0700288c2f70cf6b26e0c2de0348f",
"fields" : [ "name", "location", "contact" ],
"venue_queries" : [
{
"location" : {
"geo" : {
"$in_lat_lng_radius" : [-37.7750, 122.4183, 5000]
}
}
}]
}'
This is my attempt in Swift:
let LOCU_API_KEY = "<API_KEY>"
let centerLatitude = mapView.region.center.latitude
let centerLongitude = mapView.region.center.longitude
let arr = [centerLatitude,centerLongitude,5000]
let url = NSURL(string: "https://api.locu.com/v2/venue/search")
var request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
var params = ["api_key":LOCU_API_KEY,"fields":["name","location","contact"], "venue_queries":[["location":["geo":["$in_lat_lng_radius":arr]]]]] as [String:AnyObject!]
var err: NSError?
NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
if error == nil {
var err:NSError?
let httpResponse = response as NSHTTPURLResponse!
println(response.description)
if httpResponse.statusCode == 200 {
if var json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error:&err) as? NSDictionary {
println(json)
}
}
}
})
I only get an HTTP 400 - Bad request for this and various simplifications, like just providing the api_key. Version 1_0 of the Locu works fine, although it doesn't have the features I need.
afRequestOperationManager.GET is performing a GET request rather than a POST request.
Furthermore, the request data must be in JSON, whereas you are using URL parameters.
As others have said you need to use .POST or in someway do a POST request not a GET.
Also, it looks like this line:
let urlString = "https://api.locu.com/v2/venue/"
Should be:
let urlString = "https://api.locu.com/v2/venue/search"
Right? Notice the "search" at the end. That is why you are getting 400 I assume (404 I guess?!).
Let us know if it worked.
The '-X POST part of the curl command means you need to do an HTTP POST, not an HTTP GET.
Try using afRequestOperationManager.POST instead.