I have a problem with pushing video on the server. My API has two parameters in body: details and file and I must authorize it by token in header.
At the beginning my file was prepared and exported to the URL that I am pushing to method. Next with details (string value) I am trying to prepare it to send on the server.
After I run my app I have got this error:
Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}
Server in respond returns all data about pushed video in JSON
I used this tutorial to try make some first step:
https://mindfiremobile.wordpress.com/2014/01/27/upload-video-to-server-using-multiparts/
It my first steps with pushing video on the server and I will be glad for help.
func postDetailsWithVideo(details: String, file: NSURL) {
let url = serverURL.URLByAppendingPathComponent("api/details")
let videoData = NSData.init(contentsOfURL: file)
let kBoundary = "---------------------------14737809831466499882746641449"
let kStartTag = "--%#\r\n"
let kEndTag = "\r\n"
let kContent = "Content-Disposition: form-data; name=\"%#\"\r\n\r\n"
let body = NSMutableData()
//details data
body.appendData(String(format: kStartTag, kBoundary).dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(String(format: kContent, "storyDetails").dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(details.dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(String(format: kEndTag).dataUsingEncoding(NSUTF8StringEncoding)!)
//Video data
body.appendData(String(format: kStartTag, kBoundary).dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; file=\"flv\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: application/octet-stream\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(NSData(data: videoData!))
body.appendData(String(format: kEndTag).dataUsingEncoding(NSUTF8StringEncoding)!)
// close form
body.appendData("--\(kBoundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
let contentType = "multipart/form-data; boundary=\(kBoundary)"
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Bearer \(DataManager().getAccessToken())", forHTTPHeaderField: "Authorization")
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.HTTPBody = body
print(body)
postDataOnTheServer(request, apiMethod: "api/details")
}
func postDataOnTheServer(request: NSMutableURLRequest, apiMethod: NSString) {
let currentRequest = request
let task = NSURLSession.sharedSession().dataTaskWithRequest(currentRequest){ data, response, error in
if error != nil{
self.delegate?.errorOccured(apiMethod, error: error!)
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
self.delegate?.serverResponseFromAPIMethod(apiMethod, result: result!)
} catch {
print("response - some error")
print(error) //do something with me
}
}
task.resume()
}
Its a server issue.Ask your backend not to echo anything and you will get the response.
Related
I used JSONEncoder to get the data and used the following code to upload the receipt to the server.
// send to server
let url = URL(string: "https://ios.jobyme88.com/KidsLearnHindi/save-shopping.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// check error
if let error = error {
print ("[shark-IAP] error: \(error)")
return
}
// check response
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode) else {
print ("[shark-IAP] server error")
return
}
// print data from server
guard let data = data else {
print("[shark-IAP] no data")
return
}
let dataString = String(data: data, encoding: .utf8)
print ("[shark-IAP] got data: \(String(describing: dataString))")
}
task.resume()
But I found the string is too long that the server just receive empty string.
Do you know how to upload the receipt?
I tried this myself some time ago and worked out the following solution for sending a receipt and receiving the validation result:
Base method to create and handle the HTTP request
func sendPost(url: URL, receiptBase64String: String) {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.timeoutInterval = 15
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let postParameters = ["receipt": receiptBase64String]
request.httpBody = self.createBodyWithParameters(boundary: boundary, parameters: postParameters)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
// TODO: Handle Server response
})
task.resume()
}
Helper method to create the request body as data
public func createBodyWithParameters(boundary: String, parameters: [String: String]?) -> Data {
let body = NSMutableData()
if let parameters = parameters {
for (key, value) in parameters {
body.append(string: "--\(boundary)\r\n")
body.append(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.append(string: "\(value)\r\n")
}
}
body.append(string: "--\(boundary)--\r\n")
return body as Data
}
Extension to allow appending to NSMutableData
private extension NSMutableData {
func append(string: String) {
if let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true) {
append(data)
}
}
}
Credits to http://swiftdeveloperblog.com/image-upload-example/
Friends, I've gone through lot's of examples, which are available on S.O. Though I haven't received proper answer, and still I'm facing issue in getting data via api request using URLSession with Post request & passing parameters with it.
First, I'ld like to show you, what I have. tried till now...
func requestApiCall(){
let renewal_id = ""
let policy_no = ""
let client_name = ""
let client_id = ""
let product_name = ""
let created_date_from = ""
let created_date_to = ""
let policy_expiry_from = ""
let policy_expiry_to = ""
self.parameters = ["renewal_id":renewal_id,"policy_no":policy_no,"client_name":client_name,"client_id":client_id,"product_name":product_name,"created_date_from":created_date_from,"created_date_to":created_date_to,"policy_expiry_from":policy_expiry_from,"policy_expiry_to":policy_expiry_to]
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Accept" : "application/json",
"Content-Type" : "application/x-www-form-urlencoded"
]
let session = URLSession(configuration: config)
let Url = String(format: "http://myapi-url");
let serviceUrl = URL(string: Url)
var request = URLRequest(url: serviceUrl!)
print(request.url!)
request.httpMethod = "POST"
request.timeoutInterval = 60
request.httpBody = try! JSONSerialization.data(withJSONObject: parameters!, options: [])
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if error == nil{
print(response!)
}
else {
print(error?.localizedDescription as Any)
}
print(response!)
guard let httpResponse = response as? HTTPURLResponse, let receivedData = data
else {
print("error: not a valid http response")
return
}
switch (httpResponse.statusCode)
{
case 200: //The request was fulfilled
let response = NSString (data: receivedData, encoding: String.Encoding.utf8.rawValue)
if response == "SUCCESS"
{
print("Network - HandShaking Successfull...!!!")
}
else{
print("Network - HandShaking is not successfull...!!!")
}
case 400:
print("response-status - 400 : The request had bad syntax or was inherently impossible to be satisfied.")
case 500:
print("\nresponse-status - 500 : Internal Server Error...!!!")
default:
print("response-status - Unknown : Received Response => \(httpResponse.statusCode)")
}
})
task.resume()
}
After running above function, I'm getting httpResponse.statusCode = 500
But when I run this in postman, I get response properly, as aspected.
Postman Api-Request
Also I have tried to generate code-snippets through postman...which are as follow...
func postmanSnippetApiCall(){
let headers = [
"Content-Type": "application/x-www-form-urlencoded",
"cache-control": "no-cache",
"Postman-Token": "5d571157-86c5-4eac-ba6d-b00779ae5dbd"
]
let postData = NSMutableData(data: "renewal_id=".data(using: String.Encoding.utf8)!)
postData.append("&policy_no=".data(using: String.Encoding.utf8)!)
postData.append("&client_name=".data(using: String.Encoding.utf8)!)
postData.append("&client_id=".data(using: String.Encoding.utf8)!)
postData.append("&product_name=".data(using: String.Encoding.utf8)!)
postData.append("&created_date_from=".data(using: String.Encoding.utf8)!)
postData.append("&created_date_to=".data(using: String.Encoding.utf8)!)
postData.append("&policy_expiry_from=".data(using: String.Encoding.utf8)!)
postData.append("&policy_expiry_to=".data(using: String.Encoding.utf8)!)
postData.append("&undefined=undefined".data(using: String.Encoding.utf8)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://myapiurl")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
}
But in postman generated code snippet, I'm receiving error on this line i.e request.httpBody = postData as Data and error is this one : Cannot convert value of type 'NSMutableData' to type 'Data' in coercion
If I use thirdParty Library i.e Alamofire, then I'm able to get data very easily.
Alamofire code snippet...runs perfectly..& gives proper response.
func apiRequestByAlamofire(){
let urlString = "http://myapiurl"
let params: [String: Any]? = ["renewal_id":"","policy_no":"","client_name":"","client_id":"","product_name":"","created_date_from":"","created_date_to":"","policy_expiry_from":"","policy_expiry_to":""]
Alamofire.request(urlString, method: .post, parameters: params).responseJSON { response in
print(response) //Here getting perfect response successfully...!!!
}
}
But still I'm struggling this via URLSession...!!!
And still I doubt, that why I'm getting too much problems, while doing with URLSession.
Friends for above my doubt, please I'm open to your suggestions, as well as please help me out to understand it.
Don't know, where am I going wrong. please help me out here.
After searching and fighting a lot with this I have come up with this solution:
guard var components = URLComponents(url: URL(string: "http://example.com")!, resolvingAgainstBaseURL: true)
else { fatalError("Couldn't create URLComponents") }
components.queryItems = params.map { k, v in URLQueryItem(name: k, value: v) }
var request = URLRequest(url: baseUrl.appendingPathComponent(path.rawValue))
request.httpBody = Data(components.query!.utf8)
request.httpMethod = "POST"
The "example.com" can literally be that, because I'm just using URLComponents to encode the parameters.
I am giving you simple function, You can edit this function as per your requirement. You can change your URL and params as well. And in the response, I have written two-line if you are taking JSON array from the server then use the first one if you are taking object then second one else remove Both lines.
func abc() {
let request = NSMutableURLRequest(url: URL(string: "Your URL")!)
request.httpMethod = "POST"
let postString = "param_name_one=\( value_1 )¶m_name_two=\(value_2)&........."
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if(error != nil){
// Show Error Message
} else{
do {
//For JSON ARRAY
let jsonItem = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSArray
let json = jsonItem[0] as AnyObject
//For JSON object
let json_object = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as AnyObject
print(json_object)
} catch {
}
}
}
task.resume();
}
I'm trying to figure out how to send a photo from an iPhone to my web server.
I also need to send parameters containing the size of the photo, it's filename and other additional information about the photo in the same request as the parameter data.
The code below is on the right track I think, but where do I put the parameter data called params:
let params: Array<String> = [aI.filename, String(aI.size), String(aI.dateTime.year), String(aI.dateTime.month), String(aI.dateTime.day), String(aI.dateTime.hour), String(aI.dateTime.minute), String(aI.dateTime.second), String(aI.dateTime.millisecond)]
var serverURL = URL(string: "http://192.168.0.23/upload.php");
var req = NSMutableURLRequest(url: serverURL!, cachePolicy: NSURLRequest.CachePolicy.useProtocolCachePolicy, timeoutInterval: 60.0);
//Set request to post
req.httpMethod = "POST";
//Set content type
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type");
let task = URLSession.sharedSession().dataTaskWithRequest(req){ data, response, error in
if error != nil{
print("Error -> \(error)")
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print("Result -> \(result)")
} catch {
print("Error -> \(error)")
}
}
task.resume()
return task
Allthough some of the answers pushed me in the right direction, they still didn't fit my project and so I continued googling an I managed to find exactly what I needed in the following article: http://swiftdeveloperblog.com/image-upload-example/
I needed to make the HTTP request asynchronously and using sessions,
which I didn't specify in the question because the question was merely about how to send both several parameters along with data in one single request.
It is called Multipart Form Data when doing so.
I had to modify the code from the article a little bit to make it work for my application,
so I'm sharing my Swift 3 code below:
Trigger code
let params = [
"filename" : chunkOwner.filename ,
"size" : String(describing: chunkOwner.size) ,
"year" : String(chunkOwner.dateTime.year) ,
"month" : String(chunkOwner.dateTime.month) ,
"day" : String(chunkOwner.dateTime.day) ,
"hour" : String(chunkOwner.dateTime.hour) ,
"minute" : String(chunkOwner.dateTime.minute) ,
"second" : String(chunkOwner.dateTime.second) ,
"millisecond" : String(chunkOwner.dateTime.millisecond) ,
]
uploadChunk(url: URL(string: "http://192.168.0.23/upload.php")!, data: photoData, params: params)
Upload code:
func uploadData(url: URL, data: Data!, params: [String: String])
{
let cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData;
let request = NSMutableURLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: 6.0);
request.httpMethod = "POST";
let boundary = generateBoundaryString()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
if(data == nil) { return; }
request.httpBody = createBodyWithParameters(parameters: params, filePathKey: "file", data: data, boundary: boundary)
//myActivityIndicator.startAnimating();
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
// You can print out response object
print("******* response = \(response)")
// Print out reponse body
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("****** response data = \(responseString!)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
print(json)
}catch
{
//if you recieve an error saying that the data could not be uploaded,
//make sure that the upload size is set to something higher than the size
print(error)
}
}
task.resume()
}
func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, data: Data!, boundary: String) -> Data {
var body = Data();
if parameters != nil {
for (key, value) in parameters! {
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString(string: "\(value)\r\n")
}
}
let mimetype = "text/csv"
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(parameters!["filename"]!)\"\r\n")
body.appendString(string: "Content-Type: \(mimetype)\r\n\r\n")
body.append(data)
body.appendString(string: "\r\n")
body.appendString(string: "--\(boundary)--\r\n")
return body
}
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().uuidString)"
}
Also include the following code at the bottom of your .swift file outside of your class:
extension Data {
mutating func appendString(string: String) {
append(string.data(using: .utf8)!)
}
}
And for the PHP upload script I did some changes and now looks like this:
<?php
$target_dir = "/var/www/html/uploads";if(!file_exists($target_dir)){
mkdir($target_dir, 0777, true);
}
$target_dir = $target_dir . "/" . basename($_FILES["file"]["name"]);
echo count("size: ".$_FILES["file"]["tmp_name"]);
if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_dir)){
echo json_encode([
"Message" => "The file ". basename( $_FILES["file"]["name"]). " has been uploaded.",
"Status" => "OK",
]);
} else {
echo json_encode([
"Message" => "Sorry, there was an error uploading your file.",
"Status" => "Error",
]);
}
?>
Important Note:
Your app will fail to upload data if your server php file called
php.ini is configured to accept files smaller than the data you're
trying to upload.
For example: If php.ini is configured to accept 2 MB, then any
uploads larger than 2 MB will be ignored and your app will receive a
response saying that something went wrong.
To change the file size acceptance in php.ini you need to look for
the variable called upload_max_filesize and post_max_sizeand change those to whatever file size
your system requires.
You can put them to httpBody or to httpBodyStream (by using NSInputStream)
But don't forget to transform params for server protocol (for example xml, json, or binary data with custom format).
For your content type (application/x-www-form-urlencoded), you can find format in wikipedia:
keyName=value&keyName2=value2
The keys and values should contain of URLPathAllowedCharacterSet, to achieve it you can use stringByAddingPercentEncodingWithAllowedCharacters.
To convert the KeyValue string to NSData, you can use method dataUsingEncoding.
I am sharing you one way of posting data using NSURLConnection in Swift3
Your URL
var serverURL = URL(string: "http://192.168.0.23/upload.php")
Your parameters to be like this , just discuss with server people to which parameters you to need pass data Then assign your value to that parameter like below
serverparameter1 = \(value to post)& serverparameter2 = \(value to post2).......
With your params I did like this have a look
let params = "filename= \(aI.filename)&size = \(String(aI.size))& dateTimeYear =\(String(aI.dateTime.year))&dateTimeMonth =\(String(aI.dateTime.month))& dateTimeDay =\(String(aI.dateTime.day))&dateTimeHour =\(String(aI.dateTime.hour))&dateTimeMinute =\(String(aI.dateTime.minute))&dateTimeSecond =\(String(aI.dateTime.second))&dateTimeMilliSecond=\(String(aI.dateTime.millisecond))"
Convert your Photo Data to Base64String like below
var base64String: NSString!
let myImage = UIImage(named:"image.png")
let imageData = UIImageJPEGRepresentation(myImage, 0.9)
base64String = imageData!.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithLineFeed) as NSString!
print(base64String)
then pass as stringParameter
&ImageDataStr = \(base64String)
then final Url seems to be look like
\(serverURL)/\(params)
OR
\(serverURL)/Upload?\(params)
Step by step request
var serverURL = URL(string: "http://192.168.0.23/upload.php")
let params = "filename= \(aI.filename)&size = \(String(aI.size))& dateTimeYear =\(String(aI.dateTime.year))&dateTimeMonth =\(String(aI.dateTime.month))& dateTimeDay =\(String(aI.dateTime.day))&dateTimeHour =\(String(aI.dateTime.hour))&dateTimeMinute =\(String(aI.dateTime.minute))&dateTimeSecond =\(String(aI.dateTime.second))&dateTimeMilliSecond=\(String(aI.dateTime.millisecond))&photoDataStr = \(base64String)"
var status:NSString = "\(serverURL)/Upload?\(params)" as NSString
status = status.addingPercentEscapes(using: String.Encoding.utf8.rawValue)! as NSString
let url = URL(string: status as String)!
let request = URLRequest(url: url, cachePolicy:NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 600)
// need synchronous
Here you will get responseData
var response:URLResponse?
var responseD:Data = try! NSURLConnection.sendSynchronousRequest(request, returning:&response)
Finally make that BinaryData to readable
// save to string - the result came from the Server call
var serverResults:NSString = NSString(data: responseD, encoding: String.Encoding.utf8.rawValue)!
print(serverResults)
For Example your Result
if serverResults.range(of: "RESULT>APPROVED").location != NSNotFound
{
return "Data posted"
}
else
{
return "Failed to post"
}
I confuse by getting HTTP response in post method. when i check response on postman, then the required response is got and profile updated successfully. But in programmatically i got error notice. What was the problem? how to solve it? please, refer screenshots. Thanks in advance!
Code i have try
override func viewDidLoad() {
super.viewDidLoad()
self.updateDetails()
}
func updateDetails()
{
let postString = "api=update_people&user_id=18&email=rajesh#gmail.com&first_name=Raejsh&phone=456562&age=26&gender=male&blood_group=A"
print(postString)
// let alertMessage = alert()
let url = NSURL(string: "http://kuruthi.in/portal/api/register")
let request = NSMutableURLRequest(url: url as! URL)
request.httpBody = postString.data(using: String.Encoding.utf8)
request.httpMethod = "POST"
request.addValue("123456", forHTTPHeaderField: "X-API-KEY")
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
guard error == nil && data != nil else
{
print("Error:\(error)")
return
}
let httpStatus = response as? HTTPURLResponse
if httpStatus!.statusCode == 200
{
if data?.count != 0
{
let responseString = String(data: data!, encoding: .utf8)
print(responseString)
}
else
{
print("No data got from url!")
}
}
else
{
print("error httpstatus code")
}
}
task.resume()
}
Response in Program output :
Response in postman:
The reason you are getting html code is that you are receiving some error from your web services look at the message "A PHP Error was encountered" in your output log.
The success in postman is because your request is in form-data. Compare the request from postman from Raw with the request you are generating.
You can refer this link for creating a request. But the code is in Objective-Cand you can use this link to convert the code, or you can use Alamofire to create requests and refer this link for creating multipart form-data requests using Alamofire
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¶m2_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()
}