Swift 4 - send application/x-www-form-urlencoded JSON - json

I want to encode my JSON to application/x-www-form-urlencoded
var req = URLRequest(url: url)
req.httpMethod = "POST"
req = JsonHelper.defineCommonHeaders(request: req)
var headers = req.allHTTPHeaderFields ?? [:]
headers["Content-Type"] = "application/x-www-form-urlencoded"
print(headers)
do {
let jsonData = try encoder.encode(self)
let jsonString = String(data: jsonData, encoding: .utf8)!
req.httpBody = jsonData as Data
print ("httpBody is: ", jsonString)
} catch {
//TODO:error handling
}
sending my JSON from Postman is fine, however sending from Swift 4 does not work.
I quess I need to encode empty key somewhere. There is great example POST request using application/x-www-form-urlencoded
but it does not cover how to do it in Swift 4. What I have to change?

Related

Alamofire POST not working as expected. Error on parameters

I am trying to upload (post) a JSON string using Alamofire and I'm getting an error that leads me to believe there is some issue in the dictionary encoding.
The array I am encoding is an array of objects, all of type String. It looks like this:
class SavedDeliveries: Codable { //not sure if this is the issue (I use this to store default values locally)
var fullName = ""
var address = ""
var city = ""
var zip = ""
var phone = ""
var orders = ""
var ordersListed = ""
var pickup = ""
}
The code including the Alamofire call looks like this:
func postData() {
let headers: HTTPHeaders = [
"Content-Type": "application/json",
"X-Master-Key": "xxx", //API Key
"X-Bin-Name": "deliverydata"]
let jsonEncoder = JSONEncoder()
let jsonData = try! jsonEncoder.encode(deliveryList)
let json = String(data: jsonData, encoding: String.Encoding.utf8)
print(json!) // printing the JSON and it is correct when validated.
AF.request("https://api.jsonbin.io/v3/b", method: .post, parameters: json, encoding: JSONEncoding.default, headers: headers).responseString { (response) in
switch response.result {
case .success:
print("was successful")
case let .failure(error):
print(error)
}
}
}
I expect it to upload the JSON file but I'm getting an error message that says this:
Cannot convert value of type 'String?' to expected argument type 'Parameters?' (aka 'Optional<Dictionary<String, Any>>')
Not sure if the AF call parameter is expecting a certain kind of dictionary key:value format. If this is not the right call format for uploading JSON, what do I need to change?
Thanks for any help. I'm not a full-time Swift developer but manage an app that is usually within my capabilities. This one has me stumped.
I guess I don't understand much about HTTP requests or Alamofire, but I was able to solve this issue with the following mods to my code (without Alamofire, which seems like overkill in this case).
func postData() {
// Prepare URL
let url = URL(string: "https://api.jsonbin.io/v3/b")
guard let requestUrl = url else { fatalError() }
// Prepare URL Request Object
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
// Set HTTP Request Body
let header: HTTPHeaders = [
"Content-Type": "application/json",
"X-Master-Key": "xxx",
"X-Bin-Name": "deliverydata"
]
request.headers = header
let jsonEncoder = JSONEncoder()
let jsonData = try! jsonEncoder.encode(deliveryList)
let json = String(data: jsonData, encoding: String.Encoding.utf8)
request.httpBody = json!.data(using: String.Encoding.utf8)
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Check for Error
if let error = error {
print("Error took place \(error)")
return
}
// Convert HTTP Response Data to a String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Response data string:\n \(dataString)")
}
}
task.resume()
}
To make such a request using Alamofire, I'd recommend:
let headers: HTTPHeaders = [
"Content-Type": "application/json",
"X-Master-Key": "xxx", //API Key
"X-Bin-Name": "deliverydata"]
AF.request("https://api.jsonbin.io/v3/b",
method: .post,
parameters: deliveryList,
headers: headers).responseString { response in
switch response.result {
case .success:
print("was successful")
case let .failure(error):
print(error)
}
}
Even better, create a Decodable response type and use responseDecodable to parse it.
I would also suggest not using empty strings as your default values, that can easily lead to sending bad or invalid data to the backend.

Swift - POST request, sending JSON with Vapor 3

I have a problem sending a POST request with Vapor 3 with the body including JSON. I am using https://docs.postman-echo.com/ to test this, where it responds back with the same JSON it is sent.
I have viewed answers on here, but got errors with encoding and content types.
router.get("hooray") { req -> Future<View> in
var postHeaders: HTTPHeaders = .init()
postHeaders.add(name: .contentType, value: "application/json")
postHeaders.add(name: .accept, value: "*/*")
postHeaders.add(name: .acceptEncoding, value: "gzip, deflate")
let oneField = singleGet(foo: "barfoobar")
// { foo: "barfoobar" } - JSON string
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(oneField)
let jsonString = String(data: jsonData, encoding: .utf8)!
let postBody = HTTPBody(string: jsonString)
let httpReq = HTTPRequest(method: .POST, url: "/post")
let httpRes = HTTPClient.connect(hostname: "postman-echo.com", on: req)
.flatMap(to: singleGet.self) { client in
req.http.headers = postHeaders
req.http.contentType = .json
req.http.body = postBody
return client.send(httpReq).flatMap(to: singleGet.self) { res in
return try req.content.decode(singleGet.self)
}
}
return try req.view().render("test", httpRes)
}
struct singleGet: Content {
let foo: String
}
I am getting the correct response with this code, but I was wondering when I rework the code to match this answer, why do I get errors?
I've added the headers and body to the request and comment them out inside the closure, but it won't work this way.
let httpReq = HTTPRequest(method: .POST, url: "/post", headers: postHeaders, body: postBody)

Swift POST request to Laravel - Issue with JSON response

I am new to Swift. Trying to make a post request to laravel on localhost. To verify my Request recieved from swift within laravel. I am returning Request as JSON response. which produces this error.
Code 3840 "JSON text did not start with array or object and option to
allow fragments not set."
Which means a malformed JSON response.
Laravel UserController
public function verify_login(Request $request)
{
return response()->json($request)
}
ViewController.swift
#IBAction func verify_login(_ sender: UIButton) {
let username: String = self.username.text!
let passkey: String = self.passkey.text!
//print(username)
let urlString = "http://localhost:8888/user/verify"
guard let requestUrl = URL(string:urlString) else { return }
let parameters = "username=\(username)&passkey=\(passkey)"
var request = URLRequest(url:requestUrl)
request.httpMethod = "POST"
request.setValue("application/x-www-form-unlencoded;charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = parameters.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request) {
(data, response, error) in
if error == nil,let usableData = data {
var json: [String: Any]?
do {
json = try JSONSerialization.jsonObject(with: usableData) as? [String:Any]
print(json?["username"]! as Any)
}catch{
print(error)
}
}
}
task.resume()
}
NOTE:
Using Postman I recieve the expected response (Request object as json).
Swift end code works fine with JSON Placeholder API

swift multipart post with 2 or more base64encoded image in json param fails

Well, the request holds many params and one of them is a json string.
The problem: if the json param has more than 1 image in it (as shown below), the server side received post simply does NOT have it (the json param is not there in the post). Nevertheles, the rest of the params in the post are there in good shape (not shown in this example).
json parameter debugged:
THIS WORKS:
[{"title":"A","image":""},
{"title":"B","image":"HERE BASE64ENCODED IMAGE"}]
THIS WORKS TOO:
[{"title":"A","image":""},[{"title":"B","image":""},
{"title":"C","image":"HERE BASE64ENCODED IMAGE"}]
And these are the json params content that will be ignored in the server received post (each post has only one json param):
DONT WORK:
[{"title":"A","image":""},
{"title":"B","image":"HERE BASE64ENCODED IMAGE"},
{"title":"C","image":"HERE BASE64ENCODED IMAGE"}]
DONT WORK ALSO:
[{"title":"A","image":"HERE BASE64ENCODED IMAGE"},
{"title":"B","image":"HERE BASE64ENCODED IMAGE"}]
Every image is at most 900kb.(in my debug they maked in total 1.3MB)
EDIT: here the request:
let url:NSURL = NSURL(string: url)!
var request = URLRequest(url: url as URL)
request.httpMethod = "POST"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
My doubts are focused in the multipart-form body, here the code i am dealing with so far:
var body = Data()
for (key, value) in parameters {
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"")
if key == "json" {
body.append("\r\n")
body.append("Content-Type: text/json")
}
body.append("\r\n\r\n")
body.append("\(value)\r\n")
}
body.append("--\(boundary)--\r\n")
return body
EDIT 2: (following Dale's comment) This didnt fix it also.
body.append("--\(boundary)")
body.append("\r\n")
body.append("Content-Disposition: form-data; name=\"params\"\r\n")
body.append("Content-Type: multipart/mixed; boundary=\(boundary2)")
body.append("\r\n\r\n")
for (key, value) in parameters {
body.append("--\(boundary2)")
body.append("\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"")
body.append("\r\n\r\n")
body.append("\(value)\r\n")
}
body.append("--\(boundary2)--\r\n")
body.append("--\(boundary)--\r\n")
EDIT 3: How the json param is constructed (#OOPer):
let image = UIImage(contentsOfFile: self.getPath() // String extension
let data = UIImageJPEGRepresentation(image, 0.4)
let strBase64 = data.base64EncodedString()
var post1 = [String:Any]
post1["title"] = "AA"
post1["image"] = "\(strBase64)"
let aPosts = [[String: Any]]()
aPosts.append(post1)
aPosts.append(post2)... etc
// later on
let data = try JSONSerialization.data(withJSONObject: aPosts)
let jsonParam = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
let param["json"] = jsonParam
let parameters = [String : String]()
parameters.update(other: param)
thx

Swift2 handle data from POST

Im sending a Post message to my database with swift. But how do i handle the data from that response? My variable responseString looks like this now. I want to get each parameter and work with them.
Optional([{"id":"9","name":"hallow","strasse":"street","tel_nr":"123456789","kommentar":"comment"}])
let request = NSMutableURLRequest(URL: NSURL(string: "http://localhost/getByName.php")!)
request.HTTPMethod = "POST"
let postString = "name=hallow"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
Your php is returning an array containing a dictionary, so you'd have to access it like
if let responsedict = response.first {
print(responsedict["id"])
}// prints 9
(if you know there's going to be no other elements returned first is ok...or change how your data is sent back).
You could take it one step further and parse the response in to a model e.g.
let person = Person(id: response.first?["id"], name: response.first?["name"], etc)
print("id \(person.id)")
//id 9