How How to Perform Multiple Alamofire Requests That Are Finished One After Another

How is it possible to perform multiple Alamofire requests that are finished one after another?

The issue is just the same as in the related question you posed: the operation dependencies are on finishing an operation, as documented, but you have written code where the operation exits after asynchronously dispatching a request for future execution (the operations you created and added to a queue will finish in the order set by their dependencies, but the requests will be fired concurrently by the NSURLSession underlying Alamofire).

If you need serial execution, you can for instance do the following:

// you should create an operation queue, not use OperationQueue.main here –
// synchronous network IO that would end up waiting on main queue is a real bad idea.
let operationQueue = OperationQueue()
let timeout:TimeInterval = 30.0

for operationNumber in 0..<4 {
let operation = BlockOperation {
let s = DispatchSemaphore(value: 0)
self.performAlamofireRequest(operationNumber) { number in
// do stuff with the response.
s.signal()
}

// the timeout here is really an extra safety measure – the request itself should time out and end up firing the completion handler.
s.wait(timeout: DispatchTime(DispatchTime.now, Int64(timeout * Double(NSEC_PER_SEC))))
}

operationQueue.addOperation(operation)
}

Various other solutions are discussed in connection to this question, arguably a duplicate. There's also Alamofire-Synchronous.

Wait for multiple Alamofire Requests to finish

I've made few changes in your code:

Network class:

func response(array: [JSON]){
print(array.count)
if(array.count == path){
self.delegate?.sendJson(array)
}
}

func getMultipleRequests(_ requests: [Int]) {
DispatchQueue.global(qos: .background).async {
let group = DispatchGroup()

var array: [JSON] = []

for request in requests {

group.enter()

self.getRequest(req: request, completion: { (json) in
array.append(json)
group.leave()
})
}

group.wait()
//this line below won't be called until all entries will leave the group
self.response(array: array)
}
}

func getRequest(req: Int, completion: @escaping (_ json: JSON) -> Void) {
path = req
let rot = Router(method: .get, path: req, parameters: nil)
Alamofire.request(rot)
.response { response in
print(response.request?.url! as Any)
// check for errors
guard response.error == nil else {
// got an error in getting the data, need to handle it
print(response.error!)
let errorJson: JSON = [ "Error" : "Can't get the data!"]
completion(errorJson)
return
}
// make sure we got some JSON since that's what we expect
guard (response.data?.base64EncodedString()) != nil else {
print("Error: \(String(describing: response.error))")
let errorJson: JSON = [ "Error" : "Can't get the data!"]
completion(errorJson)
return
}
guard response.response?.statusCode == 200 else{
let errorJson: JSON = [ "Error" : "Can't get the data!"]
completion(errorJson)
return
}
let json = JSON(data: response.data!)
// get and print the title
if json != nil{
completion(json)
} else {
let errorJson: JSON = [ "Error" : "Can't get the data!"]
completion(errorJson)
return
}
}
}

So the getRequest function now have a completion block that will return a json result off each request and the function getMultipleRequests that will receive a bunch of requests from anyone

This how you can use it

Your class, that calls refresh_fiks:

@objc func refresh_fiks(){
let network = Network()
network.delegate = self
self.teams = [[]]

network.getMultipleRequests([1,2])
}

Also, instead of using group.wait() you might need to use group.notify, it's better to notify that all entries leaved the group in specified queue, like the main:

group.notify(queue: DispatchQueue.main, execute: {
print("All Done")
self.response(array: array)
})

What to read about the DispatchGroups:

RayWenderlich

ALL ABOUT SWIFT

Chain multiple Alamofire requests

Wrapping other asynchronous stuff in promises works like this:

func myThingy() -> Promise<AnyObject> {
return Promise{ fulfill, reject in
Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]).response { (_, _, data, error) in
if error == nil {
fulfill(data)
} else {
reject(error)
}
}
}
}

Edit: Nowadays, use: https://github.com/PromiseKit/Alamofire-

Alamofire number of requests one after another

Ok I ended up writing my own implementation.

I created a class RequestChain wich takes Alamofire.Request as parameter

class RequestChain {
typealias CompletionHandler = (success:Bool, errorResult:ErrorResult?) -> Void

struct ErrorResult {
let request:Request?
let error:ErrorType?
}

private var requests:[Request] = []

init(requests:[Request]) {
self.requests = requests
}

func start(completionHandler:CompletionHandler) {
if let request = requests.first {
request.response(completionHandler: { (_, _, _, error) in
if error != nil {
completionHandler(success: false, errorResult: ErrorResult(request: request, error: error))
return
}
self.requests.removeFirst()
self.start(completionHandler)
})
request.resume()
}else {
completionHandler(success: true, errorResult: nil)
return
}

}
}

And I use it like this

let r1 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in
print("1")
}

let r2 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in
print("2")
}

let r3 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in
print("3")
}

let chain = RequestChain(requests: [r1,r2,r3])

chain.start { (success, errorResult) in
if success {
print("all have been success")
}else {
print("failed with error \(errorResult?.error) for request \(errorResult?.request)")
}

}

Importent is that you are telling the Manager to not execute the request immediately

    let manager = Manager.sharedInstance
manager.startRequestsImmediately = false

Hope it will help someone else

Making multiple requests with Alamofire depending on array of chunks

Thanks to Larme's comment I was able to find my mistake. When making request to API I was passing the decoded response to the completion closure. To fix this I had to declare an array of model let responses:[SomeModel] = [] and append the decoded result to it. I used let group = DispatchGroup() so I can wait the requests to execute and have my final array of results and then I used group.notify(queue: .main, execute: {completion(.success(responses))}) to return to the main queue and have my array of completed fetched data. This is now how my code looks like:

 private func request (_ idsDict: [String: [String]], _ idSchema: String, _ completion: @escaping APIListResponseClosure<SomeModel>) -> Void {
var responses: [SomeModel] = []
let group = DispatchGroup()
let chunks = transformEntitiesIdsToChunks(idsDict)
let decoder = JSONDecoder()

chunks.forEach {chunk in
group.enter()
let parameters = constructQueryParams(idsDict, chunk, idSchema, apiKey, clientId)
AF.request(baseURL, parameters: parameters).response { response in
switch response.result {
case .success(let data):
// some error handling for decoding and no data

responses.append(data.data)
group.leave()
case .failure(let error):
return completion(.failure(.APIError(error.localizedDescription)))
}
}
}
group.notify(queue: .main, execute: {
print("Ids are fetched")
completion(.success(responses))
})
}

Thanks again to Larme and I hope I helped someone else with this case.

Wait for multiple Alamofire requests to complete before continuing?

use disptach_group_t Here is Example

dispatch_group_t group = dispatch_group_create();

__weak MainViewControllerSupplier * weakSelf = self;
dispatch_group_enter(group);
[self showHUD];
[[DataManager sharedManager] getCategoriesWithSuccessBlock:^(NSArray *categories) {
weakSelf.arrCategories = categories;
dispatch_group_leave(group);

// NSLog(@"response category= %@",categories);
} failureBlock:^(NSError *error) {
NSLog(@"response category = %@",error);
dispatch_group_leave(group);
}];

dispatch_group_enter(group);
[[DataManager sharedManager] getRegionsWithSuccessBlock:^(NSArray *regions) {
weakSelf.arrRegions = regions;
dispatch_group_leave(group);
} failureBlock:^(id error) {
NSLog(@"response region = %@",error);
dispatch_group_leave(group);

}];

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self hideHUD];
});
// All task completed

});

Alamofire multiple requests iteration

If this is not running, you could be deadlocking if you use dispatch_group_wait on the main thread, thereby blocking that thread, and preventing Alamofire from running any of its completion handlers (which also require the main thread). This is solved (assuming you are, indeed, using dispatch_group_wait), by replacing that with dispatch_group_notify.

Thus:

let group = dispatch_group_create()

for d in data {
// I enter the group
dispatch_group_enter(group)
dataService.getPoints(d.point)) { additionalPoints, error in
defer { dispatch_group_leave(group) }

guard let let additionalPoints = additionalPoints else {
print(error)
return
}

points.append(additionalPoints)
}
}

dispatch_group_notify(group, dispatch_get_main_queue()) {
// go to the next iteration here
}

Where:

func getPoints(point: WhateverThisIs, completionHandler: (JSON?, NSError?) -> ()) {
let headers = [
"Authorization": "Bearer \(token)"
]

Alamofire.request(.GET, url, headers: headers)
.responseJSON { response in
switch response.result {
case .Success:
let json = JSON(data: response.data!)
completionHandler(json, nil)
case .Failure(let error):
completionHandler(nil, error)
}
}
}

Now, I don't know what your various parameter types were, so I was left to guess, so don't get lost in that. But the key is that (a) you should make sure that all paths within your Alamofire method will call the completion handler; and (b) the caller should use dispatch_group_notify rather than dispatch_group_wait, avoiding the blocking of any threads.

Note, in order to make the completion handler callable even if the network request failed, I had to make the parameter to that closure optional. And while I was there, I added an optional error parameter, too.


A few unrelated changes included in the above sample:

  1. I'd suggest using a different variable name for the parameter of your closure. The points.append(points) in your original code snippet suggests some confusion between your points array and the points that is passed back in the closure.

  2. You don't have to set the Content-Type header, as Alamofire does that for you.

  3. I didn't change it above, but it is inefficient to use responseJSON (which uses NSJSONSerialization to parse the JSON) and then use SwiftyJSON to parse the raw data with NSJSONSerialization again. Personally, I don't bother with SwiftyJSON, but if you want to use it, I'd suggest use Alamofire's response method rather responseJSON. There's no point in parsing the JSON twice.

Alamofire callback after multiple requests finish

I finally wrapped my head around GCD handling multiple concurrent tasks on this post Dispatch Groups suggested by @GoodSp33d



Related Topics



Leave a reply



Submit