You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example using dispatch groups to execute a callback asynchronously when multiple networking requests have all finished.
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = DispatchGroup()
for i in 0 ..< 5 {
myGroup.enter()
Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}
myGroup.notify(queue: .main) {
print("Finished all requests.")
}
}
Output
Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.
Swift 3 or 4
If you don't care about orders, use @paulvs's answer, it works perfectly.
else just in case if anyone wants to get the result in order instead of fire them concurrently, here is the code.
let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "any-label-name")
let dispatchSemaphore = DispatchSemaphore(value: 0)
dispatchQueue.async {
// use array categories as an example.
for c in self.categories {
if let id = c.categoryId {
dispatchGroup.enter()
self.downloadProductsByCategory(categoryId: id) { success, data in
if success, let products = data {
self.products.append(products)
}
dispatchSemaphore.signal()
dispatchGroup.leave()
}
dispatchSemaphore.wait()
}
}
}
dispatchGroup.notify(queue: dispatchQueue) {
DispatchQueue.main.async {
self.refreshOrderTable { _ in
self.productCollectionView.reloadData()
}
}
}
Xcode 8.3.1 - Swift 3
This is the accepted answer of paulvs, converted to Swift 3:
let myGroup = DispatchGroup()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0 ..< 5 {
myGroup.enter()
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}
myGroup.notify(queue: DispatchQueue.main, execute: {
print("Finished all requests.")
})
}
import Foundation
class SimultaneousOperationsQueue {
typealias CompleteClosure = ()->()
private let dispatchQueue: DispatchQueue
private lazy var tasksCompletionQueue = DispatchQueue.main
private let semaphore: DispatchSemaphore
var whenCompleteAll: (()->())?
private lazy var numberOfPendingActionsSemaphore = DispatchSemaphore(value: 1)
private lazy var _numberOfPendingActions = 0
var numberOfPendingTasks: Int {
get {
numberOfPendingActionsSemaphore.wait()
defer { numberOfPendingActionsSemaphore.signal() }
return _numberOfPendingActions
}
set(value) {
numberOfPendingActionsSemaphore.wait()
defer { numberOfPendingActionsSemaphore.signal() }
_numberOfPendingActions = value
}
}
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
}
func run(closure: ((@escaping CompleteClosure) -> Void)?) {
numberOfPendingTasks += 1
dispatchQueue.async { [weak self] in
guard let self = self,
let closure = closure else { return }
self.semaphore.wait()
closure {
defer { self.semaphore.signal() }
self.numberOfPendingTasks -= 1
if self.numberOfPendingTasks == 0, let closure = self.whenCompleteAll {
self.tasksCompletionQueue.async { closure() }
}
}
}
}
func run(closure: (() -> Void)?) {
numberOfPendingTasks += 1
dispatchQueue.async { [weak self] in
guard let self = self,
let closure = closure else { return }
self.semaphore.wait(); defer { self.semaphore.signal() }
closure()
self.numberOfPendingTasks -= 1
if self.numberOfPendingTasks == 0, let closure = self.whenCompleteAll {
self.tasksCompletionQueue.async { closure() }
}
}
}
}
let queue = SimultaneousOperationsQueue(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
queue.whenCompleteAll = { print("All Done") }
// add task with sync/async code
queue.run { completeClosure in
// your code here...
// Make signal that this closure finished
completeClosure()
}
// add task only with sync code
queue.run {
// your code here...
}
import UIKit
class ViewController: UIViewController {
private lazy var queue = { SimultaneousOperationsQueue(numberOfSimultaneousActions: 1,
dispatchQueueLabel: "AnyString") }()
private weak var button: UIButton!
private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 100))
button.setTitleColor(.blue, for: .normal)
button.titleLabel?.numberOfLines = 0
view.addSubview(button)
self.button = button
let label = UILabel(frame: CGRect(x: 180, y: 50, width: 100, height: 100))
label.text = ""
label.numberOfLines = 0
label.textAlignment = .natural
view.addSubview(label)
self.label = label
queue.whenCompleteAll = { [weak self] in self?.label.text = "All tasks completed" }
//sample1()
sample2()
}
func sample1() {
button.setTitle("Run 2 task", for: .normal)
button.addTarget(self, action: #selector(sample1Action), for: .touchUpInside)
}
func sample2() {
button.setTitle("Run 10 tasks", for: .normal)
button.addTarget(self, action: #selector(sample2Action), for: .touchUpInside)
}
private func add2Tasks() {
queue.run { completeTask in
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + .seconds(1)) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.label.text = "pending tasks \(self.queue.numberOfPendingTasks)"
}
completeTask()
}
}
queue.run {
sleep(1)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.label.text = "pending tasks \(self.queue.numberOfPendingTasks)"
}
}
}
@objc func sample1Action() {
label.text = "pending tasks \(queue.numberOfPendingTasks)"
add2Tasks()
}
@objc func sample2Action() {
label.text = "pending tasks \(queue.numberOfPendingTasks)"
for _ in 0..<5 { add2Tasks() }
}
}
You will need to use semaphores for this purpose.
//Create the semaphore with count equal to the number of requests that will be made.
let semaphore = dispatch_semaphore_create(locationsArray.count)
for key in locationsArray {
let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
datesArray["\(key.0)"] = snapshot.value
//For each request completed, signal the semaphore
dispatch_semaphore_signal(semaphore)
})
}
//Wait on the semaphore until all requests are completed
let timeoutLengthInNanoSeconds: Int64 = 10000000000 //Adjust the timeout to suit your case
let timeout = dispatch_time(DISPATCH_TIME_NOW, timeoutLengthInNanoSeconds)
dispatch_semaphore_wait(semaphore, timeout)
//When you reach here all request would have been completed or timeout would have occurred.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With