Swift multiple requests return the same result - ios

Im making synchronous api calls to an API and in the debugger I see that I am getting different results. However my code always returns the same Id:
func fetchIDbySKU(name: String, color: String) -> Data{
let sku = name + ":" + color
let appendedsku = (sku).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let url: String = "https://www.link.com/wp-json/wc/v1/products?sku=" + appendedsku!
let semaphore = DispatchSemaphore (value: 0)
var result = Data()
var request = URLRequest(url: URL(string: url)! ,timeoutInterval: Double.infinity)
request.addValue("Basic secret token", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
print(String("______________________________________"))
print(String(data: data, encoding: .utf8)!)
result = data
print(String("______________________________________"))
semaphore.signal()
}
task.resume()
semaphore.wait()
return result
}
so result is always returning the same result even though I in the console I an see that the response data is different.
This is the code where I am expecting the return:
var request_data = fetchIDbySKU(name: name, color: color_name)
var product_id = String(request_data[0])
parts_list.append(Item(product_id: product_id, quantity: quantity))

Your semaphore is initialized every time the function is called. Just take out the initialization of your semaphore out of the function and it will work fine.
let semaphore = DispatchSemaphore (value: 0)
func fetchIDbySKU(name: String, color: String) -> Data{
let sku = name + ":" + color
let appendedsku = (sku).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let url: String = "https://www.link.com/wp-json/wc/v1/products?sku=" + appendedsku!
var result = Data()
var request = URLRequest(url: URL(string: url)! ,timeoutInterval: Double.infinity)
request.addValue("Basic secret token", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
print(String("______________________________________"))
print(String(data: data, encoding: .utf8)!)
result = data
print(String("______________________________________"))
semaphore.signal()
}
task.resume()
semaphore.wait()
return result
}

Related

Get request doesn't get executed in swift, getting nil value error where URL is passed in the request

I am trying to add a GET request in my application.
The values or the final url string is flowing correctly in my sURL variable. But still while executing this code I get "Found nil error" at line - "var request = URLRequest(url: URL(string: sUrl)!)"
Please help.
My code -
class AllStickerService {
static let allStickerInstance: AllStickerService = AllStickerService()
var delegateAllSticker: AllStickerProtocol!
func fetchAllSticker(category: String, APITokenString: String) {
var sUrl = "http://xyzabc.com/api/stickers"
let params = ["category": category]
var sParams = ""
for (key,value) in params {
sParams += key + "=" + value
print("\(key), \(value)")
}
if !sParams.isEmpty {
sParams = "?" + sParams
sUrl = sUrl + sParams
}
var request = URLRequest(url: URL(string: sUrl)!)
print(request)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Bearer "+APITokenString, forHTTPHeaderField: "Authorization")
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if (response as? HTTPURLResponse) != nil {
if let httpResponse = response as? HTTPURLResponse {
print("statusCode: \(httpResponse.statusCode)")
print(httpResponse)
}
if let data = data{
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
print(json)
}catch {
print("Error\(error)")
}
}
}
}.resume()
}
}
Force unwrapping is rarely a good idea. Either is using string concatenation to create URLs; Aside from potential security problems, you have to worry about things like url encoding, which is your problem here.
Your category parameter value has a space, this needs to be encoded as %20, but you don't do this and you end up with an invalid URL string. You don't see this with Postman because it is encoding the space for you behind the scenes.
A better approach is to use URLComponents, URLQueryItem and use conditional unwrapping
func fetchAllSticker(category: String, APITokenString: String) {
var sUrl = "http://xyzabc.com/api/stickers"
let params = URLQueryItem(name:"category", value: category)
if var urlComponents = URLComponents(string:"http://xyzabc.com/api/stickers") {
urlComponents.queryItems = params
if let url = urlComponents.url {
var request = URLRequest(url: url)
...
}
}
I would also recommend you look into using Decodable to handle your JSON response rather than JSONSerialization

How to Pass Key Value Parameter in JSON POST method in Swift?

This is API http://itaag-env-1.ap-south-1.elasticbeanstalk.com/filter/taggedusers/
its parameter: "contactsList" : ["5987606147", "6179987671", "5082508888"]
its header: ["deviceid": "584D97F-761A-4C24-8C4B-C145A8B8BD75", "userType": "personal", "key": "9609cc826b0d472faf9967370c095c21"]
In my code if i put breakpoint then filtertaggedUser() is calling but i am unable to go inside completionHandler the access is not going inside dataTask
Access going to else part why? the api is working.
i am trying to pass parameter key value in URL string like below
let urlStr = "http://itaag-env-1.ap-south-1.elasticbeanstalk.com/filter/taggedusers/?contactsList=" + "8908908900"
is this correct approch?
code for above API:
func filtertaggedUser() {
print("inside filter taggedusers")
let headers = ["deviceid": "584D97F-761A-4C24-8C4B-C145A8B8BD75", "userType": "personal", "key": "9609cc826b0d472faf9967370c095c21"]
let urlStr = "http://itaag-env-1.ap-south-1.elasticbeanstalk.com/filter/taggedusers/?contactsList=" + "8908908900"
let request = NSMutableURLRequest(url: NSURL(string:urlStr)! as URL,cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
// access not coming here
let httpResponse = response as? HTTPURLResponse
if httpResponse!.statusCode == 200 {
print("filter taggedusers inside")
do {
print("filter taggedusers inside do")
let jsonObject = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! [String :AnyObject]
print("filter taggedusers \(jsonObject)")
} catch { print(error.localizedDescription) }
} else { Constants.showAlertView(alertViewTitle: "", Message: "Something went wrong, Please try again", on: self) }
})
dataTask.resume()
}
OUTPUT:
POSTMAN OUTPUT
POSTMAN Body
why response is not coming, where i did mistake, please help me with the code.
We can call the Post request API like below,
func getPostString(params:[String:Any]) -> String
{
var data = [String]()
for(key, value) in params
{
data.append(key + "=\(value)")
}
print(data.map { String($0) }.joined(separator: "&"))
return data.map { String($0) }.joined(separator: "&")
}
func callPostApi(){
let url = URL(string: "http://itaag-env-1.ap-south-1.elasticbeanstalk.com/filter/taggedusers/")
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
request.setValue("584D97F-761A-4C24-8C4B-C145A8B8BD75", forHTTPHeaderField: "deviceid")
request.setValue("9609cc826b0d472faf9967370c095c21", forHTTPHeaderField: "key")
request.setValue("personal", forHTTPHeaderField: "userType")
let parameters = getPostString(params: ["contactsList":["8908908900"]])
request.httpBody = parameters.data(using: .utf8)
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
let httpResponse = response as? HTTPURLResponse
print(httpResponse!.statusCode)
// 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()
}
Output :
{"8908908900":{"userId":"9609cc826b0d472faf9967370c095c21","userName":"Satish Madhavarapu","profilePic":null,"oniTaag":true,"tagged":false,"userType":"personal"}}

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
})

Datatask with semaphore not working as intended

I'm trying to make a URL request but waiting for it to be done prior to letting another URL request go through by using semaphore. Seems like the following code waits for semaphore timeout (10 seconds) and only then the datatask response happens.
In other words, execution does not go to semaphore.signal() before timeout happens. If I set the timeout to "distantFuture", the execution hangs.
static func makeHTTPPostRequestJsonWait(path: String, body: [String: AnyObject], onCompletion: #escaping ServiceResponse) {
var request = URLRequest(url: URL(string: path)!)
let semaphore = DispatchSemaphore(value: 0)
// Set the method to POST
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let json = JSON(body)
let jsonString = json.rawString()
// Set the POST body for the request
let jsonBody = jsonString?.data(using: String.Encoding.utf8)
request.httpBody = jsonBody
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
semaphore.signal()
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
onCompletion(json, nil)
} else {
onCompletion(nil, error)
}
}
task.resume()
_ = semaphore.wait(timeout: DispatchTime.now() + 10)
}

Swift - Multiple Parameters to webservice

I have the following code that should send a username and password off to a webservice, in return I get a single integer back:
func attemptLogin() {
let url:URL = URL(string: endpoint+"/LoginNew")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let postString = "username="+txtUsername.text! + "; password="+txtPassword.text!
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest) {
(
data, response, error) in
guard let data = data, let _:URLResponse = response, error == nil else {
print("error")
return
}
let dataString = String(data: data, encoding: String.Encoding.utf8)
print(dataString)
}
task.resume()
}
In my function I need to add two parameters are I'm trying to do in this line:
let postString = "username="+txtUsername.text! + "; password="+txtPassword.text!
request.httpBody = postString.data(using: String.Encoding.utf8)
I am getting the following response from my web service when I run this however
Optional("Missing parameter: password.\r\n")
I am obviously not appending the parameters to the request properly but I'm not sure what I've done wrong.
It is good practice to avoid using explicit unwraps of optionals (using !), use guard let for text i UITextFields instead.
And why not separate into two methods, attemptLogin and login, which maybe can take a closure for code to execute when sign in completed? Maybe the closure can take an Result enum.
Like this:
typealias Done = (Result) -> Void
enum MyError: Error {
case unknown
}
enum Result {
case success(String)
case failure(MyError)
init(_ error: MyError) {
self = .failure(error)
}
init(_ dataString: String) {
self = .success(dataString)
}
}
func login(username: String, password: String, done: Done? = nil) {
let session = URLSession.shared
guard
let url = URL(string: endpoint+"/LoginNew"),
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let postString = "username=\(username)&password=\(password)"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request) {
(data, response, error) in
guard let data = data else { done?(Result(.unknown)); return }
let dataString = String(data: data, encoding: String.Encoding.utf8)
done?(Result(dataString))
}
task.resume()
}
func attemptLogin() {
guard
let username = txtUsername.text,
let password = txtPassword.text
else { return }
login(username: username, password: password) {
result in
swicth result {
case .success(let dataString):
print(dataString)
case .failure(let error):
print("Failed with error: \(error)")
}
}
}
Disclaimer: Have not tested the code above, but hopefully it compiles (at least with very small changes).

Resources