How to post the data in encoding structure? - ios

I am using two models GET and POST which are encodable and decodable
using the below code i am able to get the data in GET model, but i am not able to post the data with POST model.
please guide me how to post the data using POST model
let url = URL(string: "<YOUR URL HERE>")
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Accept" : "application/json",
"Content-Type" : "application/x-www-form-urlencoded"
]
let session = URLSession(configuration: config)
var request = URLRequest(url: url!)
request.encodeParameters(parameters: ["username": username, "password":
password])
let task = session.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let sentPost = try JSONDecoder().decode(Get.self, from: data)
print(sentPost)
} catch {}
}
task.resume()
model
struct Post: Encodable, Decodable {
let username: String
let password: String
}
extension for URLRequest
extension URLRequest {
private func percentEscapeString(_ string: String) -> String {
var characterSet = CharacterSet.alphanumerics
characterSet.insert(charactersIn: "-._* ")
return string
.addingPercentEncoding(withAllowedCharacters: characterSet)!
.replacingOccurrences(of: " ", with: "+")
.replacingOccurrences(of: " ", with: "+", options: [], range: nil)
}
mutating func encodeParameters(parameters: [String : String]) {
httpMethod = "POST"
let parameterArray = parameters.map { (arg) -> String in
let (key, value) = arg
return "\(key)=\(self.percentEscapeString(value))"
}
httpBody = parameterArray.joined(separator: "&").data(using: String.Encoding.utf8)
}
}

You don't need to create another string and then to data. This will be handled by Codable protocol and your JSONEncoder. You need to encode your post type using JSONEncoder and provide the data to the request.httpBody
Try this:
let url = URL(string: "<YOUR URL HERE>")
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Accept" : "application/json",
"Content-Type" : "application/x-www-form-urlencoded"
]
do {
let aPost = Post(username: "username", password: "password")
let encoder = JSONEncoder()
let encodedPost = try encoder.encode(aPost)
let session = URLSession(configuration: config)
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.httpBody = encodedPost
let task = session.dataTask(with: request) { data, response, error in
do {
let sentPost = try JSONDecoder().decode(Get.self, from: data)
print(sentPost)
} catch {
print(error.localizedDescription)
}
}
task.resume()
} catch {
print(error.localizedDescription)
}

Related

How to validate that my API Code is returning data

I just want to know if this code I wrote is returning data and I wanted the community to help achieve it since I am new to swift, I don't know where to put the print() function just to see if data is being shown on CLI output
class APICaller {
static let shared = APICaller()
private let baseURL = "http://000.000.000.000:0000/api/"
private init() {}
func getVehicles(for id: String, IMEI: Int, completed: #escaping (Result<[Vehicles],OnTraErrors>) -> Void ){
let endpoint = baseURL + "GetVehicles/?UserIdentificationValue=GASGSDG43848B497FE4604352GGS"
let headers = [
"content-type": "application/json",
"authorizetoken": "NjQzODM2N0NDNDM4NDhCNDk3RkU0NjGSAG4sg3=",
"cache-control": "no-cache",
]
guard let url = URL(string: endpoint) else {
completed(.failure(.invalidURL))
return
}
var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 120)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let vehicles = try decoder.decode([Vehicles].self, from: data)
completed(.success(vehicles))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
}

How to implement the Bearer Token to validate the API url

I set up the API and all, the only thing is Bearer Token I couldn't find any information about any code on how to implement it so it can validate the URL I am using as API.
do I need to create new swift file just for bearer token or I can write the code to the API swift file "the code below is api file"
static let shared = APICaller()
private let baseURL = "http://000.000.000.000:3030/api/"
private init() {}
var vehicles = [Vehicles]()
func getVehicles(for id: String, IMEI: Int, completed: #escaping (Result<[Vehicles],Errors>) -> Void ){
let endpoint = baseURL + "GetVehicle/?UserIdentificationValue=346HIU4623UIHG3I3I&IMEI=216216123612"
guard let url = URL(string: endpoint) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.vehicles = try JSONDecoder().decode([Vehicles].self, from: data)
DispatchQueue.main.async {
completed(.failure(.invalidData))
}
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
Thanks In Advance
Also I am new to swift so I would appreciate if you can tell me my API code is correct or needs any fixes since its about receiving some car info and putting into a table view cell :)
I have attached the request including headers in which you need to pass Bearer token like did in below code
let headers = [
"content-type": "application/json",
"authorizetoken": "NjQzOPA2N0NDNDFAH4CNDk3R23F2FQUY0NjV3FFE=",
"cache-control": "no-cache",
]
let parameters = ["id":"123456789"] as [String : Any]
let postData = try? JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "Your URL")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 120.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as? Data
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData

send multi part form data in swift

I used this code below to send multipart params
let headers = [
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer \(myToken)",
"cache-control": "no-cache"
]
let parameters = [
[
"name": "firstname",
"value": "alex"
],
[
"name": "lastname",
"value": "black"
],
[
"name": "birthdate_day",
"value": "1"
],
[
"name": "birthdate_month",
"value": "5"
],
[
"name": "birthdate_year",
"value": "1989"
],
[
"name": "gender",
"value": "m"
],
[
"name": "avatar",
"fileName": "\(imageURL)"
]
]
let boundary = "Boundary-\(NSUUID().uuidString)"
var body = ""
let error: NSError? = nil
for param in parameters {
let paramName = param["name"]!
body += "--\(boundary)\r\n"
body += "Content-Disposition:form-data; name=\"\(paramName)\""
if let filename = param["fileName"] {
if let contentType = param["content-type"] {
do {
let fileContent = try String(contentsOfFile: filename, encoding: String.Encoding.utf8)
if (error != nil) {
print(error as Any)
}
body += "; filename=\"\(filename)\"\r\n"
body += "Content-Type: \(contentType)\r\n\r\n"
body += fileContent
} catch {
print(error)
}
}
} else if let paramValue = param["value"] {
body += "\r\n\r\n\(paramValue)"
}
}
let postData = NSMutableData(data: body.data(using: String.Encoding.utf8)!)
let request = NSMutableURLRequest(url: NSURL(string: "myUrl")! 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 as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse?.statusCode as Any)
}
})
dataTask.resume()
return dataTask
the image url and the rest of the data But I will receive Satus code 500 I know that this error is server side But the android version is using the same api url and that's working well I know that this code can be fix and maybe small changes can fix this code for working
use URL instead of NSURL
var request = URLRequest is mutable, use this instead of NSMutableURLRequest
var data = Data() is mutable, use this instead of NSMutableData
append the file blob data safely using Data(contentsOf:options:) method
content-type is missing in parameters, so if let contentType = param["content-type"] { ... } will fail to proceed, using application/octet-stream default mime type
depending on the server, it might be necessary to provide a filename for the uploads
I fixed all above concerns and moved the URLRequest.httpBody generating code to following extension.
extension URLRequest {
private func formHeader(_ name: String, crlf: String, fileName: String? = nil, mimeType: String? = nil) -> String {
var str = "\(crlf)Content-Disposition: form-data; name=\"\(name)\""
guard fileName != nil || mimeType != nil else { return str + crlf + crlf }
if let name = fileName {
str += "; filename=\"\(name)\""
}
str += crlf
if let type = mimeType {
str += "Content-Type: \(type)\(crlf)"
}
return str + crlf
}
private func getFileUrl(_ file: Any) -> URL? {
if let url = file as? String {
return URL(string: url)
}
return file as? URL
}
private func getFileData(_ url: URL) -> Data? {
do {
return try Data(contentsOf: url, options: .mappedIfSafe)
} catch {
print(error)
return nil
}
}
mutating func setPost(body parameters: [[String: Any]]) {
let boundary = "Boundary+\(arc4random())\(arc4random())"
self.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
data.append("--\(boundary)".data(using: .utf8)!)
let crlf = "\r\n"
for parameter in parameters {
guard let paramName = parameter["name"] as? String else { continue }
if let value = parameter["value"] {
let header = formHeader(paramName, crlf: crlf)
data.append("\(header)\(value)".data(using: .utf8)!)
} else if let file = parameter["file"], let fileUrl = getFileUrl(file), let fileData = getFileData(fileUrl) {
let fileName = parameter["fileName"] as? String
let contentType = parameter["content-type"] as? String
let header = formHeader(paramName, crlf: crlf, fileName: fileName ?? fileUrl.lastPathComponent, mimeType: contentType ?? "application/octet-stream")
data.append(header.data(using: .utf8)!)
data.append(fileData)
} else {
print("\(paramName): empty or invalid value")
continue
}
data.append("\(crlf)--\(boundary)".data(using: .utf8)!)
}
data.append("--\(crlf)".data(using: .utf8)!)
self.httpBody = data
self.httpMethod = "POST"
}
}
Usage
let parameters = [
["name": "firstname", "value": "alex"],
["name": "avatar", "file": URL],
["name": "avatar", "file": "file:///", "fileName": "image.png", "content-type": "image/png"]
]
request.setPost(body: parameters)
Note above in parameters
file key represents either a URL object or file path String.
fileName: image.png is for backend, represents name of the file.
Finally add headers and create URLSession.shared.dataTask as your original code.
Update-2 function instead of an extension
func getParameterData(_ name: String, parameter: [String : Any]) -> Data? {
var str = "\r\nContent-Disposition: form-data; name=\"\(name)\""
if let value = parameter["value"] {
return "\(str)\r\n\r\n\(value)".data(using: .utf8)!
}
guard
let file = parameter["file"],
let url = (file is String ? URL(string: file as! String) : file as? URL)
else {
return nil
}
let data: Data
do {
data = try Data(contentsOf: url, options: .mappedIfSafe)
} catch {
print(error)
return nil
}
let fileName = (parameter["fileName"] as? String) ?? url.lastPathComponent
str += "; filename=\"\(fileName)\"\r\n"
let contentType = (parameter["content-type"] as? String) ?? "application/octet-stream"
str += "Content-Type: \(contentType)\r\n"
return (str + "\r\n").data(using: .utf8)! + data
}
func setPostRequestBody(_ request: inout URLRequest, parameters: [[String: Any]]) {
let boundary = "Boundary+\(arc4random())\(arc4random())"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
data.append("--\(boundary)".data(using: .utf8)!)
for parameter in parameters {
guard
let name = parameter["name"] as? String,
let value = getParameterData(name, parameter: parameter)
else {
continue
}
data.append(value)
data.append("\r\n--\(boundary)".data(using: .utf8)!)
}
data.append("--\r\n".data(using: .utf8)!)
request.httpBody = data
}
Usage-2
var request = URLRequest(url: URL(string: "myUrl")!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
setPostRequestBody(&request, parameters: [
["name": "firstname", "value": "alex"],
["name": "avatar", "file": URL object or path String]
])
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard error != nil else {
print(error!.localizedDescription)
return
}
let statusCocde = (response as? HTTPURLResponse)?.statusCode
print(statusCode ?? 0)
if let data = data {
print(String(data: data, encoding: .utf8) ?? "")
}
}
dataTask.resume()

Unable to post parameters in post request ios swift

I'm trying to send these parameters as a post request to the URL but the parameters are not getting sent. I don't know whether is URLSession configuration issue. Can anyone check and solve the issue?
import UIKit
let json: [String: Any] = [
"set_id" : "20",
"user_id" : "30",
"type" : "contact",
"contact_name" : "shinto"
]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
var str = String(data: jsonData!, encoding: .utf8)
let url = URL(string: "****.php")!
var request = URLRequest(url: url)
request.httpMethod = "Post"
request.httpBody = str!.data(using: .utf8)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) {
(data, response, error) in
if let data = data {
if let postResponse = String(data: data, encoding: .utf8) {
print(postResponse)
}
}
}
task.resume()
Check with this:
func post method(){
let headers = [
"Content-Type": "application/json",
"cache-control": "no-cache"]
let parameters = ["set_id" : "20",
"user_id" : "30",
"type" : "contact",
"contact_name" : "shinto"] as [String : Any]
let postData = try? JSONSerialization.data(withJSONObject: parameters, options: [])
let url = URL(string: "****.php")!
var request = URLRequest(url: url)
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)
}
guard let httpresponse = response as? HTTPURLResponse,
(200...299).contains(httpresponse.statusCode) else {
print ("server error")
return
}
if let mimeType = response?.mimeType,
mimeType == "application/json",
let data = data,
let dataString = String(data: data, encoding: .utf8) {
print ("got data: \(dataString)")
}
}
})
dataTask.resume()
}
I used an online service called RequestBin to inspect your request and data seem to be sent correctly. I only did minor modifications as already mentioned in the comment.
This was the resulting code:
let json: [String: Any] = [
"set_id" : "20",
"user_id" : "30",
"type" : "contact",
"contact_name" : "shinto"
]
let jsonData = try! JSONSerialization.data(withJSONObject: json)
let url = URL(string: "http://requestbin.fullcontact.com/***")! // Was "using"
var request = URLRequest(url: url)
request.httpMethod = "POST" // Was "Post"
request.httpBody = jsonData // Was utf8 string representation
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) {
(data, response, error) in
if let data = data {
if let postResponse = String(data: data, encoding: .utf8) {
print(postResponse)
}
}
}
task.resume()
You can check inspected result using this service. You simply create a new URL and use it in your request. After you have successfully sent the request all you do is reload the page to inspect your request.
Note that these are "http" requests so you need to allow arbitrary loads.
You may set your request like following and change content type according to your need
import UIKit
let json: [String: Any]? = [
"set_id" : "20",
"user_id" : "30",
"type" : "contact",
"contact_name" : "shinto"
]
let url = URL(string: "****.php")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
if let parameters = json
{
self.makeParamterString(request: request, parameterDic: parameters)
}
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) {
(data, response, error) in
if let data = data {
if let postResponse = String(data: data, encoding: .utf8) {
print(postResponse)
}
}
}
task.resume()
static func makeParamterString(request:NSMutableURLRequest, parameterDic:[String:AnyObject])
{
let _ = NSCharacterSet(charactersIn:"=\"#%/<>?#\\^`{|}").inverted
var dataString = String()
for (key, value) in parameterDic {
dataString = (dataString as String) + ("&\(key.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)=\(value)")
}
dataString = dataString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
request.httpBody = dataString.data(using: String.Encoding.utf8)
}

Post request with HTTP header parameters

I Want to use Bittrex api. I've read their api docs. There are explanations like the following.
For this version, we use a standard HMAC-SHA512 signing. Append apikey
and nonce to your request and calculate the HMAC hash and include it
under an apisign header.
$apikey='xxx';
$apisecret='xxx';
$nonce=time();
$uri='https://bittrex.com/api/v1.1/market/getopenorders?apikey='.$apikey.'&nonce='.$nonce;
$sign=hash_hmac('sha512',$uri,$apisecret);
$ch = curl_init($uri);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('apisign:'.$sign));
$execResult = curl_exec($ch);
$obj = json_decode($execResult);
I want to do this with Swift. But I don't want to use Alamofire.
I wrote a code. I think I'm doing everything but I'm getting the following error.
{
message = "APISIGN_NOT_PROVIDED";
result = "<null>";
success = 0;
}
I wrote similar code with Delphi. It works fine. So there is no problem with APIKEY. When I use the same parameters in Delphi, the same SecretHex is generated. So there's no problem with Encryption.
I think, I cannot do the Post Request with headers.
I can not find the fault. Would you please help me.
func getBalances()
{
let apiKeySTR = "01235xxxxxx"
let secretSTR = "41691xxxxxx"
let path = "https://bittrex.com/api/v1.1/account/"
let timeInterval = NSDate().timeIntervalSince1970
let epochtime = String(floor(timeInterval))
let urlFull = path + "getbalances" + "?" + "apikey=" + apiKeySTR + "&" + "nonce=" + epochtime
let secretUInt8 : [UInt8] = Array(urlFull.utf8)
var secretKey : [UInt8]?
do {
try secretKey = HMAC(key: secretSTR, variant: .sha512).authenticate(secretUInt8)
} catch {
print ("Error")
}
let secretHex = secretKey?.toHexString() ?? ""
guard let url = URL(string: urlFull) else { return }
var request = URLRequest(url: url)
request.addValue("apising", forHTTPHeaderField: (secretHex))
request.httpMethod = "POST"
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
}
First off... you have a typo:
request.addValue("apising", forHTTPHeaderField: (secretHex))
I believe it's apisign, not "apising", right?
And below is a recap on creating REST API requests with a header and body. You can update this method according your needs:
1) Create URLRequest
var request = URLRequest(url: requestURL)
2) Set headers and http method:
request.allHTTPHeaderFields = ["Authentication" : "Bearer XYZ..."]
request.httpMethod = "POST"
3) Set request body:
// parameters is a simple [String:String] dictionary, just as header
let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
request.httpBody = jsonData
Complete example:
public enum RESTMethod:String {
case get = "GET"
case post = "POST"
case put = "PUT"
}
public func sendRequest(_ url: String,
method: RESTMethod,
headers: [String : String],
parameters: [String : Any],
completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionTask! {
let requestURL: URL
if method == .get {
let parameterString = parameters.stringFromHttpParameters()
requestURL = URL(string:"\(url)?\(parameterString)")!
} else {
requestURL = URL(string: url)!
}
var request = URLRequest(url: requestURL)
request.allHTTPHeaderFields = headers
request.httpMethod = method.rawValue
if method == .post {
let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
request.httpBody = jsonData
}
request.timeoutInterval = 60
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
completionHandler(data,response,error)
}
task.resume()
return task
}
extension Dictionary {
/// Build string representation of HTTP parameter dictionary of keys and objects
func stringFromHttpParameters() -> String {
let parameterArray = self.map { (key, value) -> String in
let percentEscapedKey = (key as! String).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
let percentEscapedValue = (value as? String ?? "\(value)").addingPercentEncodingForURLQueryValue()!
return "\(percentEscapedKey)=\(percentEscapedValue)"
}
return parameterArray.joined(separator: "&")
}
}
Usage:
sendRequest("http://yourserver",
method: .get, // .post or .put
headers: [],
parameters: [],
completionHandler: { (data, response, error) in
// Handle response here
})

Resources