I am hitting a web service url 10 times and getting the response. I am using Alamofire
and SwiftyJSON
. This is my controller code
class ViewController: UIViewController {
let dispatchGroup = DispatchGroup()
var weatherServiceURL = "http://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22"
override func viewDidLoad() {
super.viewDidLoad()
start()
}
func start() {
weatherService()
dispatchGroup.notify(queue: .main) {
print("All services complete")
}
}
func weatherService() {
for i in 1...10 {
dispatchGroup.enter()
APIManager.apiGet(serviceName: self.weatherServiceURL, parameters: ["counter":i]) { (response:JSON?, error:NSError?, count:Int) in
if let error = error {
print(error.localizedDescription)
return
}
guard let response = response else { return }
print("\n\(response) \n\(count) response\n")
self.dispatchGroup.leave()
}
}
}
}
This is my Service Handler class code
class APIManager: NSObject {
class func apiGet(serviceName:String,parameters: [String:Any]?, completionHandler: @escaping (JSON?, NSError?, Int) -> ()) {
Alamofire.request(serviceName, method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
if let data = response.result.value{
let json = JSON(data)
completionHandler(json,nil, parameters!["counter"] as! Int)
}
break
case .failure(_):
completionHandler(nil,response.result.error as NSError?, parameters!["counter"] as! Int)
break
}
}
}
}
I am sending a counter key with the index of for loop just to keep the track of response of which index is coming back. But the response is not coming in serial order. We can expect 3rd response before the 2nd and 1st response. This is because the API call with APIManager.apiGet
function call is asynchronous and is escaping and therefore continuing the for loop.
Also I used the dispatchQueue
let dispatchQueue = DispatchQueue(label: "com.test.Queue", qos: .userInteractive)
and converted the function as:
func weatherService() {
for i in 1...10 {
dispatchGroup.enter()
dispatchQueue.async {
APIManager.apiGet(serviceName: self.weatherServiceURL, parameters: ["counter":i]) { (response:JSON?, error:NSError?, count:Int) in
if let error = error {
print(error.localizedDescription)
return
}
guard let response = response else { return }
print("\n\(response) \n\(count) response\n")
self.dispatchGroup.leave()
}
}
}
}
Same result as the service calling code is asynchronous. If we make
dispatchQueue.sync {
//service call
}
then also we will not get the response in serial order since the networking call in async and dispatchQueue assumes the task is completed.
Condition is to hit the service in async manner only without freezing the UI. If I hit the service is synchronous manner, then I get my desired result. But blocking main thread is not at all acceptable.
I can manage this thing using array or some global bool variables, but I don't want to use them. Is there any other way I can get response in serial order in which it is called? Any help or hint is appreciated.
Solution: Use
DispatchSemaphore
s and aDispatchQueue
Rather than saving the closures, I decided to wrap everything up in a dispatch queue and use semaphores inside it
Output is this always