I'm trying to write to a database and not able to get POST data from Swift into PHP. I've checked everything along the way and the data is present in request.httpBody before sending the request, but I get this error
"SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'order_id' cannot be null"
Also PHP prints an empty array of $request
Any help is appreciated. Racking my brain on this one!
Here's the code
SWIFT:
func saveNewProduct(product: ProductClass) {
//declare parameter as a dictionary which contains string as key and value combination.
let productDict: [String:Any] = ["order_id": product.order_id!,
"product_sku": product.product_sku!,
"product_description": product.product_description!,
"product_base_price": product.product_base_price!,
"product_price": product.product_price!,
"product_min_qty": product.product_min_qty!,
"product_qty": product.product_qty!,
"product_subtotal": product.product_subtotal!,
"product_imageURL": product.product_imageURL!]
let urlString: String = "https://www.mywebsite.com/dbinsertproduct.php"
guard let url = URL(string: urlString) else {
print("Error: cannot create URL")
return
}
//create the session object
let session = URLSession.shared
//create the URLRequest object using the url object
var request = URLRequest(url: url)
//set http method as POST
request.httpMethod = "POST"
do {
request.httpBody = try? JSONSerialization.data(withJSONObject: productDict, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request, completionHandler: {
data,response,error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
PHP:
<?php include("dbclass.php"); ?>
<?php
$postdata = file_get_contents("php://input");
$request = json_decode($postdata, true);
$newProduct = new DBConnector('mysql.mysite.com', 'username', 'password', 'mysiteorders');
$p = $newProduct->dbInsertProduct($request);
error_log($p);
echo ($p);
?>
More PHP:
public function dbInsertProduct($request)
{
try {
$stmt = $this->prepare("INSERT INTO `products` (`order_id`,`product_sku`,`product_description`,`product_base_price`,`product_price`,`product_notes`,`product_min_qty`,`product_qty`,`product_subtotal`,`product_imageURL`) VALUES (:order_id,:product_sku,:product_description,:product_base_price,:product_price,:product_notes,:product_min_qty,:product_qty,:product_subtotal,:product_imageURL);");
$stmt->bindParam(':order_id', $request['order_id']);
$stmt->bindParam(':product_sku', $request['product_sku']);
$stmt->bindParam(':product_description', $request['product_description']);
$stmt->bindParam(':product_base_price', $request['product_base_price']);
$stmt->bindParam(':product_price', $request['product_price']);
$stmt->bindParam(':product_notes', $request['product_notes']);
$stmt->bindParam(':product_min_qty', $request['product_min_qty']);
$stmt->bindParam(':product_qty', $request['product_qty']);
$stmt->bindParam(':product_subtotal', $request['product_subtotal']);
$stmt->bindParam(':product_imageURL', $request['product_imageURL']);
error_log( print_r($request, TRUE) );
$stmt->execute();
} catch(PDOException $e) {
error_log($e->getMessage());
echo json_encode($e->getMessage());
}
}
UPDATE:
The request shows up as GET, not POST (verified with Charles proxy).
As you can see in the code, I'm setting request.httpMethod = "POST". Am I missing something else??
I found that the POST was being changed to GET upon redirect from www.mysite.com to mysite.com. Removing the 'www' did the trick.
Related
I have an app in xcode where I will be taking notes and uploading them to a server on the web. I have the following code in xcode to POST. I'm getting an error
The data couldn’t be read because it isn’t in the correct format.
Here are my parameters to upload to php
let parameters: [String: Any] = ["title": title, "post": post]
Here is my create Post code
func createPost(parameters: [String: Any]) {
guard let url = URL(string: "\(prefixUrl)/post.php") else {
print("Did not find url")
return
}
let data = try! JSONSerialization.data(withJSONObject: parameters)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = data
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request ) { (data, res, error) in
if error != nil {
print("error", error?.localizedDescription ?? "")
return
}
do {
if let data = data {
let result = try JSONDecoder().decode([postDataBase].self, from: data)
DispatchQueue.main.sync {
print(result)
}
}else {
print("No Data")
}
} catch let JsonError {
print("fetch json error:", JsonError.localizedDescription)
}
}.resume()
}
The create post is wrapped in a class ViewDataBase with other CRUD commands...
I have a struct postDataBase where i know the error The data couldn’t be read because it isn’t in the correct format. could be happening as well. I've changed these to various data types with no success
struct postDataBase: Decodable {
var ID: String
var title: String
var post: String
}
My php code is here. I use bluehost to support my database so i can have a public domain. If you need my username and password let me know i can change it later.
<?php
$connection=mysqli_connect("localhost","****","****");
$ID = $_POST['ID'];
$title = $_POST['title'];
$post = $_POST['post'];
if(!$connection)
{
die('Connection Failed');
}
else
{
$dbconnect = #mysqli_select_db('mlbroadv_PlantAssistDB', $connection);
if(!$dbconnect)
{
die('Could not connect to Database');
}
else
{
$query = "INSERT INTO Notes (title, post) VALUES ( '$title', '$post');";
mysqli_query($query, $connection) or die(mysqli_error());
echo 'Successfully added.';
echo $query;
}
}
?>
My goal is getting my swift data uploaded to php and into mySQL database. There seems to be a disconnect somewhere...
I think the error lies in the following code where the php files is read in to connect with mySQL and to upload my variables...
EDIT:
let data = try! JSONSerialization.data(withJSONObject: parameters)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = data
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request ) { (data, res, error) in
EDIT 2: Still Trying to get it
For this line...
let parameters: [String: Any] = ["title": "Fgh", "post": "Fgg"
I get...
Param raw data is coded as: ["post": "Fgg", "title": "Fgh"]
For this line...
let dataParam = try! JSONSerialization.data(withJSONObject: parameters)
I Get...
json serialization request is coded as: 28 bytes
For this line...
URLSession.shared.dataTask(with: request as URLRequest ) { (data, res, error) in
if error != nil {
print("urlsession error is coded as: ", error?.localizedDescription ?? "")
return
}
I Get for the data varaible...
urlsession data is coded as: 235 bytes
and for the res variable, I get the following repsonse...
urlsession results is coded as: <NSHTTPURLResponse: 0x282ea64c0> { URL: https://mlbroadvisions.com/post.php } { Status Code: 200, Headers {
"Content-Encoding" = (
gzip
);
"Content-Type" = (
"text/html; charset=UTF-8"
);
Date = (
"Fri, 22 Jul 2022 16:07:03 GMT"
);
Server = (
cloudflare
);
Vary = (
"Accept-Encoding"
);
"cf-cache-status" = (
DYNAMIC
);
"cf-ray" = (
"72ed6d53f9072be1-ORD"
);
"expect-ct" = (
"max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""
);
"host-header" = (
"c2hhcmVkLmJsdWVob3N0LmNvbQ=="
);
} }
during the ...
catch let error as NSError {
print(error.localizedDescription)
It gives me the error...
The data couldn’t be read because it isn’t in the correct format.
json data is coded as: 235 bytes
What part of my code do i need to change to get it into the correct format i've tried a lot of things none of which worked.
I hope you all are doing well and staying safe! :)
I am new to iOS programming. I am trying to post values from a textfield to a MYSQL Database. As a first step, I tried to just print the values received on the PHP end.
The values are printed on the Swift end but on the PHP end, an empty string(null) is received. Could you please help and let me know why the string received on PHP end is null? Please help! I am stuck and unsure of what to do next.
I have tried retrieving values from the database and that code works perfectly fine.
Thanks in advance!
EDITED:
This has been now fixed. The correct code is below -
Swift Code:
struct DatatoPost : Codable{
var name : String
init() {
name = "Empty String"
}
}
var myDatatoPost = DatatoPost()
//Protocol created for this structure so that it can be applied to multiple class (View Controllers) that create a delegate for it(ManageDataDelegate)
protocol ManageDataDelegate {
func updateViewController(_ myManager : ManageData, saveOutputData: OutputData)
func didFailError(error : Error)
}
/* what action is expected to be taken on data reached through PHP file*/
struct ManageData {
var delegate : ManageDataDelegate?
var data : Data
init() {
data = Data()
}
//create POST URL based on function - this is the URL to post data to the backend.
mutating func postPHPData(){
let urlPostString = myConstant.baseURL + myConstant.postPHPValue
print(urlPostString)
performPostOperation(urlPostString)
}
//Steps to create URL and other related objects - prepare data for parsing.
mutating func performPostOperation(_ urlPostString : String) {
//1. Create URL
if let url = URL(string: urlPostString){
//Create request variable
var request : URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
do{
let params : [String : Encodable] = ["name": myDatatoPost.name]
data = try JSONSerialization.data(withJSONObject: params, options: .init())
let body = Data(data).base64EncodedData()
print("Data is: ")
let dataString = String(data: data, encoding: .utf8)
print(dataString)
request.httpBody = body
let requestString = String(data: request.httpBody!, encoding: .utf8)
print("Request String is:")
print(requestString)
//2. Create URLSession
let session = URLSession(configuration: .default)
//3. Give the session a task
let task = session.dataTask(with: request as URLRequest, completionHandler: handlePOST(data:response:error:))
//4. Start the session
task.resume()
}catch{
print(error)
}
}
}
//Function that lists the activities that need to be completed in the URL session - GET Data.
func handlePOST(data : Data?, response : URLResponse?, error: Error?){
if error != nil {
delegate?.didFailError(error: error!)
print("error is")
print(error!)
return
}
if let safeData = data{
let dataString = String(data: safeData, encoding: .utf8)
print(dataString)
}
print(response)
}
}
PHP Code:
<?php
echo "we are here";
// Create connection
$con = mysqli_connect("localhost","username","password","dbname");
echo "we are here again";
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: ";
}
$postdata = json_decode(file_get_contents("php://input"),TRUE);
$decoded = base64_decode($postdata);
echo "decoded!";
print_r($decoded,true);
//this value is not printed
echo "postdata";
echo $postdata->name;
//this value is not printed
$name= $postdata["name"];
echo "Name";
echo $postdata["name"];
//this value is not printed
echo "Base 64";
echo base64_decode($postdata["name"]);
echo base64_decode($postdata->name);
//This value is not printed
if (empty($postdata["name"])){
echo "String is empty";
//this is printed
}
if (empty($postdata->name)){
echo "String is empty";
//This is printed
}
echo "Its done";
// Close connections
mysqli_close($con);
?>
You have specified your request’s Content-Type to be application/json. So it should be JSON, free of base64 encoding. The only time you should use base64-encoding is if you are trying to send binary data in JSON. (And even then, in that case, we would reach for application/x-www-form-urlencoded request, not application/json request.)
Anyway, to send JSON request and parse JSON response:
protocol ManageDataDelegate: class {
func didFailError(error: Error)
func didReceiveResponse(object: Any)
}
/* what action is expected to be taken on data reached through PHP file*/
struct ManageData {
weak var delegate: ManageDataDelegate? // delegates should always be weak to avoid strong reference cycle; obviously `ManageDataDelegate` must be class protocol
//create POST URL based on function - this is the URL to post data to the backend.
func postPHPData(name: String) {
let urlPostString = URL(string: MyConstant.baseURL)!.appendingPathComponent(MyConstant.postPHPValue)
print(urlPostString)
performPostOperation(urlPostString, name: name)
}
//Steps to create URL and other related objects - prepare data for parsing.
func performPostOperation(_ url: URL, name: String) {
//Create request variable
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
do {
let params = ["name": name]
let body = try JSONSerialization.data(withJSONObject: params)
// 1. for diagnostic purposes, let's look at the JSON
if let json = String(data: body, encoding: .utf8) {
print("JSON is:", json)
}
request.httpBody = body
//2. Create URLSession
let session = URLSession(configuration: .default)
//3. Give the session a task
let task = session.dataTask(with: request, completionHandler: handlePOST(data:response:error:))
//4. Start the session
task.resume()
//5. If creating a URLSession locally, make sure to invalidate it when the request is done or else you will leak memory; better, create one `URLSession` and reuse it for all of your requests (and then you don't need/want to invalidate it)
session.finishTasksAndInvalidate()
} catch {
print(error)
}
}
enum ManageDataError: Error {
case unknownError(Data?, URLResponse?, Error?)
case notJsonResponse(String)
}
//Function that lists the activities that need to be completed in the URL session - GET Data.
func handlePOST(data: Data?, response: URLResponse?, error: Error?) {
guard let responseData = data, error == nil else {
let error = error ?? ManageDataError.unknownError(data, response, error)
delegate?.didFailError(error: error)
print("error is")
print(error)
return
}
if let object = try? JSONSerialization.jsonObject(with: responseData) {
delegate?.didReceiveResponse(object: object)
} else if let string = String(data: responseData, encoding: .utf8) {
delegate?.didFailError(error: ManageDataError.notJsonResponse(string))
} else {
delegate?.didFailError(error: ManageDataError.unknownError(data, response, error))
}
print(response ?? "No response")
}
}
There are a variety of refinements buried in the above (if creating a session, you must invalidate it when finished; passing responses to the delegate; making sure delegate reference is weak to avoid strong reference cycles; etc.), but hopefully this illustrates the idea: No base64-encoding is needed or desired with JSON requests.
Note, you have specified the request’s Accept to also be application/json so I would make sure it did precisely that:
<?php
header("Content-Type: application/json");
// Create connection
$con = mysqli_connect("localhost","username","password","dbname");
// Check connection
if (mysqli_connect_errno())
{
$result = [
"success" => false,
"message" => "Failed to connect to MySQL: " . mysqli_connect_error()
];
echo json_encode($result, JSON_PRETTY_PRINT);
exit();
}
$postdata = json_decode(file_get_contents("php://input"), true);
if (isset($postdata["name"]))
{
$result = [
"success" => true,
"name" => $postdata["name"]
];
}
else
{
$result = [
"success" => false,
"message" => "'name' not found",
"postdata" => $postdata
];
}
echo json_encode($result, JSON_PRETTY_PRINT);
// Close connections
mysqli_close($con);
?>
So, this parses the JSON and checks for name, but returning a JSON response, which is what the client was looking for.
There are other refinements I would suggest (e.g. I would use JSONEncoder and JSONDecoder rather than JSONSerialization; not create new URLSession for every request; usually a “network controller” like this would be a reference type so that you can use the same one wherever needed; I would use a framework such as Laravel in the server code, etc.), but I did not want to stray too far from the question at hand.
The code I've posted here is a shortened versions with just the essentials to the issue. When put through rigorous error testing, it appears that there are no issues either within the request, within the server, or within the return code, except for the fact that the response from the server says "Function": null instead of "Function": "addUser" as it should. Is there something I'm doing wrong on either end? It just seems to not be recognizing the posted variables, but I've used this code numerous times in other apps and servers and it seems to be failing here for some reason I cannot see. Thank you for any assistance.
Here is my PHP on the server:
<?php
$t = $_POST["function"];
$do = array("Success"=>true, "Function"=> $t);
echo json_encode($do);
?>
Here is the swift I'm using to make the request:
let params = ["function": "addUser"] as [String: AnyObject]?
fetchData("https://pdt.pitchprogress.net/SamplePHP.php", token: nil, parameters: params, method: "POST", onCompletion: { (success, data) -> Void in
if success {
do {
let json = try JSON(data: data!)
if json["Success"].boolValue == true {
print("success!")
print(json.description)
print(json["Function"].stringValue)
}
}
}
})
func fetchData(_ feed:String,token:String? = nil,parameters:[String:AnyObject]? = nil,method:String? = nil, onCompletion:#escaping (_ success:Bool,_ data:Data?)->Void){
DispatchQueue.main.async {
if let unwrapped_url = URL(string: feed){
let request = NSMutableURLRequest(url: unwrapped_url)
if let parm = parameters {
if let data = (try? JSONSerialization.data(withJSONObject: parm, options:[])) as Data? {
request.httpBody = data
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("\(data.count)", forHTTPHeaderField: "Content-Length")
}
}
if let unwrapped_method = method {
request.httpMethod = unwrapped_method
}
let sessionConfiguration = URLSessionConfiguration.default
sessionConfiguration.timeoutIntervalForRequest = 15.0
let session = URLSession(configuration: sessionConfiguration)
let taskGetCategories = session.dataTask(with: request as URLRequest, completionHandler: { (responseData, response, error) -> Void in
let statusCode = (response as! HTTPURLResponse?)?.statusCode
//println("Status Code: \(statusCode), error: \(error)")
if error != nil || (statusCode != 200 && statusCode != 201 && statusCode != 202){
onCompletion(false, nil)
} else {
onCompletion(true, responseData)
}
})
taskGetCategories.resume()
}
}
}
This is because you are sending the params as JSON while your API requires params as form data.
This code sends the params as simple form post and this retrieved successful response.
var request = URLRequest(url: URL(string: "http://app123.freeiz.com/Apis/samples/api4.php")!)
request.httpMethod = "POST"
var postString = ""
postString.append("function=value") // replace 'function' with your paramname and 'value' with your value'
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
// check for fundamental networking error
print("error=\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
// check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
let responseString = String(data: data, encoding: .utf8)
if let unWrappedResponseString = responseString{
print(unWrappedResponseString)
}
}
task.resume()
I had once made a small wrapper for sending simple form data, If i find it I will update the answer with that. Till then you can try with this. Let me know how it goes..
Looks like your key is different use "function" instead of using "Function"
Hello I have update my old swift app in the new version of swift 3, The code was connected to a php page by passing values in post and then returning a json message since I updated the app to swift 3 xcode me from the following errors, How can i fix these errors?
Error:
Swift Code:
let URL_SAVE_TEAM = "http://localhost/ios-login.php"
var email:String = "";
var password:String = "";
func PrintValue(){
// print(username);
//print(password);
}
func Login() -> Bool{
//created NSURL
let requestURL = NSURL(string: URL_SAVE_TEAM)
//creating NSMutableURLRequest
let request = NSMutableURLRequest(URL: requestURL)
//setting the method to post
request.HTTPMethod = "POST"
//getting values from text fields
//creating the post parameter by concatenating the keys and values from text field
let postParameters = "email="+email+"&password="+password;
//adding the parameters to request body
request.HTTPBody = postParameters.dataUsingEncoding(NSUTF8StringEncoding)
//creating a task to send the post request
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil{
print("error is \(error)")
return;
}
//parsing the response
do {
//converting resonse to NSDictionary
let myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
//parsing the json
if let parseJSON = myJSON {
//creating a string
var msg : String!
//getting the json response
msg = parseJSON["message"] as! String?
//printing the response
print(msg)
}
} catch {
print(error)
}
}
//executing the task
task.resume()
return false;
}
Xcode Image of Error:
PHP CODE:
<?php
header('Content-Type: application/json');
$email= $_POST['email'];
$password = $_POST['password'];
$ris='Ti rispondo dal server zio';
echo json_encode($ris);
// echo "prova";
?>
I would write this more swiftly :)
func Login() -> Bool{
//created URL
guard let requestURL = URL(string: URL_SAVE_TEAM) else { return false }
//creating URLRequest
var request = URLRequest(url: requestURL)
//setting the method to post
request.httpMethod = "POST"
//getting values from text fields
//creating the post parameter by concatenating the keys and values from text field
let postParameters = "email=\(email)&password=\(password)"
//adding the parameters to request body
request.httpBody = postParameters.data(using: .utf8)
//creating a task to send the post request
let session = URLSession.shared
let task = session.dataTask(with: request) {
data, response, error in
guard error == nil else {
print("error is \(error!.localizedDescription)")
return
}
guard let data = data else {
print("No data was returned by the request!")
return
}
//parsing the response
do {
//converting resonse to NSDictionary
let myJSON = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? Dictionary<String, String?>
//parsing the json
guard let parseJSON = myJSON, let msg = parseJSON["message"] as? String else {
print("Error parsing data")
return
}
//printing the response
print(msg)
} catch {
print(error)
}
}
//executing the task
task.resume()
return false
}
You may think to add a completion handler to your function to handle the login success!
I can't figure out how to properly send POST parameters.
My Swift 3:
let parameters = ["name": "thom", "password": "12345"] as Dictionary<String, String>
let url = URL(string: "https://mywebsite.com/test.php")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do
{
request.httpBody = try JSONSerialization.data(withJSONObject: parameters)
}
catch let error
{
print(error.localizedDescription)
}
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler:
{
data, response, error in
guard error == nil else
{
print(error as Any)
return
}
guard let data = data else
{
return
}
do
{
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]
{
print(json)
print(json["post"]!)
}
else
{
print("no json")
}
}
catch let error
{
print(error.localizedDescription)
}
})
task.resume()
My PHP:
<?php
header('Content-Type: application/json');
if(empty($_POST)) echo json_encode(array('post'=>'empty'));
else echo json_encode($_POST+array('post'=>'not_empty'));
exit;
If I set the content-type header (in Swift) to application/json I get:
["post": empty]
empty
If I set it to application/x-www-form-urlencoded I get:
["{\"name\":\"thom\",\"password\":\"12345\"}": , "post": not_empty]
not_empty
How do I send the dictionary to my server as $_POST key/value pairs, not as a json_encoded string?
You want to percent-escape the request into a x-www-form-urlencoded request, like so:
let parameters = ["name": "thom", "password": "12345"]
let url = URL(string: "https://mywebsite.com/test.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.updateHttpBody(with: parameters)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = session.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
// handle response here
}
task.resume()
Where
extension URLRequest {
/// Populate the `httpBody` of `application/x-www-form-urlencoded` request.
///
/// - parameter parameters: A dictionary of keys and values to be added to the request
mutating func updateHttpBody(with parameters: [String : String]) {
let parameterArray = parameters.map { (key, value) -> String in
return "\(key.addingPercentEncodingForQueryValue()!)=\(value.addingPercentEncodingForQueryValue()!)"
}
httpBody = parameterArray.joined(separator: "&").data(using: .utf8)
}
}
extension String {
/// Percent escape value to be added to a HTTP request
///
/// This percent-escapes all characters besides the alphanumeric character set and "-", ".", "_", and "*".
/// This will also replace spaces with the "+" character as outlined in the application/x-www-form-urlencoded spec:
///
/// http://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
///
/// - returns: Return percent escaped string.
func addingPercentEncodingForQueryValue() -> String? {
let generalDelimitersToEncode = ":#[]#?/"
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return addingPercentEncoding(withAllowedCharacters: allowed)?.replacingOccurrences(of: " ", with: "+")
}
}