Datatask with semaphore not working as intended - ios

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

Related

Swift multiple requests return the same result

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
}

Why not getting response from one URL?

I am using URLSession. I am not receiving any error or response.
It works with one url. However it does not work with one another.
I have also tried percentencoding. But it doesn't work too.
The code is below
let urlString = "https://stark-spire-93433.herokuapp.com/json"//This is not working
//let urlString = "https://jsonplaceholder.typicode.com/todos"//This is working
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)//URLSession.shared
var request = URLRequest(url: URL(string:urlString)!)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: [], options: [])
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
print("response---",response)
print("error--",error)
if data != nil {
let json = try? JSONSerialization.jsonObject(with: data!)
print("JSOn",json)
} else {
print("error data is nil")
}
})
task.resume()
Too cumbersome code.
This is sufficient
let url = URL(string:"https://stark-spire-93433.herokuapp.com/json")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { print(error!); return }
do {
let json = try JSONSerialization.jsonObject(with: data)
print("JSOn",json)
} catch {
print(error)
}
}
task.resume()

Swift 3: URLSession / URLRequest Not Working

I am still trying to convert our application from Swift 2 over to Swift 3 because I am being forced to since all of our Apple devices are now running iOS 10.
I have gone through the code conversion and thought I was doing well however, while attempting to debug my JSON issues (posted in another question), I am now dealing with requests not even being sent.
let params: [String:AnyObject] = [
"email":"\(self.preferences.string(forKey: "preference_email")!)" as AnyObject
]
let requestParams: [String:AnyObject] = [
"action":"601" as AnyObject,
"params":params as AnyObject
]
do {
let requestObject = try JSONSerialization.data(withJSONObject: requestParams, options:[])
var request = URLRequest(url: URL(string: "http://domain.tld/path/")!)
request.httpBody = requestObject
request.httpMethod = "POST"
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
NSLog("Got here?")
session.dataTask(with: request) {data, response, error in
guard let data = data, error == nil else {
print("error=\(error)")
return
}
NSLog("Got here 3?")
let object:JSON = JSON(data:data)
NSLog("Object: \(object)")
}.resume()
NSLog("Got here 4?")
} catch {
NSLog("Got here catch?")
}
NSLog("End of getUser")
The code above yields the following output:
2016-10-04 13:00:12.011969 OneTouch[1589:623015] [DYMTLInitPlatform] platform initialization successful
2016-10-04 13:00:12.264319 OneTouch[1589:622954] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2016-10-04 13:00:12.265321 OneTouch[1589:622954] [MC] Reading from public effective user settings.
2016-10-04 13:00:12.295055 OneTouch[1589:622954] Got here?
2016-10-04 13:00:12.295445 OneTouch[1589:622954] Got here 4?
2016-10-04 13:00:12.295515 OneTouch[1589:622954] End of getUser
(lldb)
Which means that the request isn't even being made. Is there some key that I have to add to the PLIST again? This is starting to get annoying.
Below is my old code and it isn't even working anymore:
let params: [String:AnyObject] = [
"email":"\(self.preferences.string(forKey: "preference_email")!)" as AnyObject
]
let requestParams: [String:AnyObject] = [
"action":"601" as AnyObject,
"params":params as AnyObject
]
do {
let requestObject = try JSONSerialization.data(withJSONObject: requestParams, options:[])
let request = NSMutableURLRequest(url: URL(string: "http://domain.tld/path/" as String)!, cachePolicy:NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData, timeoutInterval: 20)
request.httpBody = requestObject
request.httpMethod = "POST"
NSLog("Got here?")
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {data, response, error in
if error != nil {
NSLog("Got here 2?")
}
NSLog("Got here 3?")
let object:JSON = JSON(data:data!)
NSLog("Object: \(object)")
})
NSLog("Got here 4?")
task.resume()
} catch {
NSLog("Got here catch?")
}
NSLog("End of getUser")
The code above yields the same output as the other code does!
If you put a breakpoint immediately after calling getUser, the URLSession task's completion handler, which runs asynchronously (i.e. generally finishes later, unless the request failed immediately or was satisfied by some cached response) may not have had a chance to be called.
If you put a breakpoint inside the dataTask completion handler, you should see your data at that point.
Personally, I'd make sure to give getUser a completion handler so you know when it's done:
func getUser(completionHandler: #escaping (JSON?, Error?) -> Void) {
let params = [
"email":"\(preferences.string(forKey: "preference_email")!)"
]
let requestParams: [String: Any] = [
"action": "601",
"params": params
]
do {
let requestObject = try JSONSerialization.data(withJSONObject: requestParams)
var request = URLRequest(url: URL(string: "http://domain.tld/path/")!, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 20)
request.httpBody = requestObject
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) {data, response, error in
guard let data = data, error == nil else {
completionHandler(nil, error)
return
}
completionHandler(JSON(data: data), nil)
}
task.resume()
} catch {
completionHandler(nil, error)
}
}
Then when you call it, you can do something like:
getUser { json, error in
guard let json = json else {
print(error)
return
}
// do something with json
print(json)
}
And just put your breakpoint in getUser's completion handler. And remember that you have no assurances that the completion handler will run on the main queue or not, so you'll want to make sure to dispatch and UI or model updates back to the main queue.

Controlling tasks within Swift 2.0

Working in Swift 2.0, on IOS 9.2.1 using Xcode 7.2
Learning Swift 2.0 and I have written a routine that creates a NSURL Session, gets back some JSON data and then parses it. It works great...
BUT I some help in understanding how make this work as in get the outer function, share_list_folders to wait until the task here truly completes so I can return the result?
var parsedJson:[String:AnyObject] = [:]
func shared_list_folders() {
// **** list_folders (shared) ****
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.dropboxapi.com/2/sharing/list_folders")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("Bearer ab-XXX", forHTTPHeaderField: "Authorization")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
//print("Response: \(response)")
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Body: \(strData)\n\n")
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers);
self.jsonParser(jsonResult)
for (key, value) in self.parsedJson {
print("key2 \(key) value2 \(value)")
}
} catch {
print("Bang")
}
})
task.resume()
let string2return = parsedJson["path_lower"] as? String
return(string2return)!
}
Its not really a completion, cause task will go off and do its own thing and share_list_folders will complete? Obviously I don't get the path_lower value here, until its too late... looked at delegates? And I tried, but then I run into issued with the completion block...
As you noticed NSURLSession after resuming it's task go away and do it's job which requires time. It's part of asynchronous programming when you have to deal with situation when something is calculated/prepared in another thread. Under the hood NSURLSession has own thread and there is waiting for server response. Then invokes completionHandler on main thread. That's a simplified theory. Going back to your question:
It's obviously that you have to wait for server response. As in another languages code is executed line by line, so you can't return anything as you wrote. The answer is: use closures.
Function you declare could use delegate as well but let's focus on closures:
func shared_list_folders(completion: (string: String?, error: ErrorType?) -> Void) {
// **** list_folders (shared) ****
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.dropboxapi.com/2/sharing/list_folders")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("Bearer ab-XXX", forHTTPHeaderField: "Authorization")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
//print("Response: \(response)")
if let error = error {
completion(string: nil, error: error)
return
}
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Body: \(strData)\n\n")
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers);
let parsedJson = self.jsonParser(jsonResult) // I assume this returns parsedJson?
let string2return = parsedJson["path_lower"] as? String
completion(string: string2return, error: nil)
} catch { // here we have an error
completion(string: nil, error: error)
}
})
task.resume()
}

sendAsynchronousRequest was deprecated in iOS 9, How to alter code to fix

Below is my code I am getting the issue with:
func parseFeedForRequest(request: NSURLRequest, callback: (feed: RSSFeed?, error: NSError?) -> Void)
{
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in
if ((error) != nil)
{
callback(feed: nil, error: error)
}
else
{
self.callbackClosure = callback
let parser : NSXMLParser = NSXMLParser(data: data!)
parser.delegate = self
parser.shouldResolveExternalEntities = false
parser.parse()
}
}
}
This is now deprecated as of iOS 9, and is telling me to use dataTaskWithRequest instead. Can someone help me change sendAsync with dataTask, I don't know how to.
Use NSURLSession instead like below,
For Objective-C
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:"YOUR URL"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
}] resume];
For Swift,
var request = NSMutableURLRequest(URL: NSURL(string: "YOUR URL")!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.HTTPBody = try? NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")})
task.resume()
For asynchronously query, from Apple docs
Like most networking APIs, the NSURLSession API is highly
asynchronous. It returns data in one of two ways, depending on the
methods you call:
To a completion handler block that returns data to your app when a
transfer finishes successfully or with an error.
By calling methods on your custom delegate as the data is received.
By calling methods on your custom delegate when download to a file is
complete.
Swift implementation
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
}
Swift 3.0
var request = URLRequest(url: URL(string: "http://example.com")!)
request.httpMethod = "POST"
let session = URLSession.shared
session.dataTask(with: request) {data, response, err in
print("Entered the completionHandler")
}.resume()
This is the swift 2.1 version:
let request = NSMutableURLRequest(URL: NSURL(string: "YOUR URL")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")})
task.resume()
Swift 2.0:
Old (replace with New below):
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data, error) -> Void in
// Code
}
New:
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
// Code
}
task.resume()
Swift 4
let params = ["email":"email#email.com", "password":"123456"] as Dictionary<String, String>
var request = URLRequest(url: URL(string: "http://localhost:8080/api/1/login")!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
do {
let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary<String, AnyObject>
print(json)
} catch {
print("error")
}
})
task.resume()
with swift 3.1
let request = NSMutableURLRequest(url: NSURL(string: image_url_string)! as URL)
let session = URLSession.shared
request.httpMethod = "POST"
let params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
print("Response: \(String(describing: response))")})
task.resume()
Illustrating with an example, the alternative code to the deprecation of:
sendAsynchronousRequest(_:queue:completionHandler:)' was deprecated in iOS 9.0: Use [NSURLSession dataTaskWithRequest:completionHandler:]
Tested and works in Swift 2.1 onwards.
import UIKit
class ViewController: UIViewController {
#IBOutlet var theImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://upload.wikimedia.org/wikipedia/commons/6/6a/Johann_Sebastian_Bach.jpg")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) -> Void in
if error != nil {
print("thers an error in the log")
} else {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data: data!)
self.theImage.image = image
}
}
}
task.resume()
}
}
//Displays an image on the ViewControllers ImageView. Connect an outlet of the ImageView
Here is the SWIFT3.0 Version of Nilesh Patel's Answer with JSONSerialised data
let url = URL(string: "<HERE GOES SERVER API>")!
var request = URLRequest(url: url)
request.httpMethod = "POST" //GET OR DELETE etc....
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("<ValueforAuthorization>", forHTTPHeaderField: "Authorization")
let parameter = [String:Any]() //This is your parameters [String:Any]
do {
let jsonData = try JSONSerialization.data(withJSONObject: parameter, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
request.httpBody = jsonData
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request, completionHandler: { (incomingData, response, error) in
if let error = error {
print(error.localizedDescription)
print(request)
}else if let response = response {
print(response)
}else if let incomingData = incomingData {
print(incomingData)
}
})
task.resume()
} catch {
print(error.localizedDescription)
}
Swift 4.2
This worked for me:
func loadImageFromURL(URL: NSURL) {
let request = URLRequest(url: URL as URL)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let imageData = data {
DispatchQueue.main.async {
self.imageView.image = UIImage(data: imageData)
}
}
}
task.resume()
}
I had to add "DispatchQueue.main.async { }" because I had a runtime warning, since only the main thread is supposed to modify UI elements.

Resources