Chain multiple Alamofire requests
Wrapping other asynchronous stuff in promises works like this:
func myThingy() -> Promise {
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-
How to synchronize multiple Alamofire requests
You have to use DispatchGroup, and don't forget about deadlocks.
var data1: MyData?
var data2: MyData?
var data3: MyData?
func makeRequest(url: String, completion: (result: ResponseResult, data: MyData?) -> Void){
Alamofire.request(.GET, url).responseJSON { response in
switch response.result {
case .Success(let JSON):
completion(result: .Success, MyData(JSON))
case. Failure(let error):
completion(result: .Failure, nil)
}
}
}
let downloadGroup = DispatchGroup()
downloadGroup.enter()
downloadGroup.enter()
downloadGroup.enter()
makeRequest(url1){ result, data in
data1 = data
downloadGroup.leave()
}
makeRequest(url2){ result, data in
data2 = data
downloadGroup.leave()
}
makeRequest(url3){ result, data in
data3 = data
downloadGroup.leave()
}
DispatchQueue.global(qos: .background).async {
downloadGroup.wait()
DispatchQueue.main.async {
workWithData(data1, data2: data2, data3: data3)
}
}
How to handle multiple network call in Alamofire
Your assessment is 100% correct. At the moment, the two options you laid out are really the only possible approaches. I agree with you that your second option is much better than the first given your use case.
If you wish to combine ReactiveCocoa with Alamofire, then that's certainly possible, but hasn't been done yet to my knowledge. You could also investigate whether PromiseKit would be able to offer some assistance, but it hasn't been glued together with Alamofire yet either. Trying to combine either of these libraries with the Alamofire response serializers will not be a trivial task by any means.
Switching gears a bit, I don't really think ReactiveCocoa or PromiseKit are very well suited for your use case since you aren't chaining service calls, you are running them in parallel. Additionally, you still need to run all your parsing logic and determine whether each one succeeded or failed and then update your application accordingly. What I'm getting at is that Option 2 is going to be your best bet by far unless you want to go to all the effort of combining PromiseKit or ReactiveCocoa with Alamofire's response serializers.
Here's what I would suggest to keep things less complicated.
import Foundation
import Alamofire
class ParallelServiceCaller {
var firstServiceCallComplete = false
var secondServiceCallComplete = false
func startServiceCalls() {
let firstRequest = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["first": "request"])
firstRequest.responseString { request, response, dataString, error in
self.firstServiceCallComplete = true
self.handleServiceCallCompletion()
}
let secondRequest = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["second": "request"])
secondRequest.responseString { request, response, dataString, error in
self.secondServiceCallComplete = true
self.handleServiceCallCompletion()
}
}
private func handleServiceCallCompletion() {
if self.firstServiceCallComplete && self.secondServiceCallComplete {
// Handle the fact that you're finished
}
}
}
The implementation is really clean and simple to follow. While I understand your desire to get rid of the completion flags and callback function, the other options such as ReactiveCocoa and/or PromiseKit are still going to have additional logic as well and may end up making things more complicated.
Another possible option is to use dispatch groups and semaphores, but that really adds complexity, but could get you much closer to a ReactiveCocoa or PromiseKit styled approach.
I hope that helps shed some light.
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
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
Related Topics
Check If Property Is Set in Core Data
What's the Best Way to Handle Multiple Skscenes
Property Not Working with Getter and Setter
Inconsistent Unicode Emoji Glyphs/Symbols
Swift Editor Placeholder in Source File
Secitemadd and Secitemcopymatching Returns Error Code -34018 (Errsecmissingentitlement)
My Prerelease App Has Been "Processing" for Over a Week in Itunes Connect, What Gives
Uitableview Row Animation Duration and Completion Callback
An App Id with Identifier '' Is Not Available. Please Enter a Different String
Prevent Segue in Prepareforsegue Method
What's the Best Way to Find the User's Documents Directory on an Iphone
Connect Objective-C Framework to Swift iOS 8 App (Parse Framework)
Swift 3.0 Error: Escaping Closures Can Only Capture Inout Parameters Explicitly by Value
Testing Corelocation on iPhone Simulator
How to Display .Svg Image Using Swift
Update Restkit 'Lcl_Rk.H' File Not Found in Rklog.H
How to Insert the Uitextview into Uialertview in iOS
What Is the '' Is or the ':' at the Equation When Coding in Swift