Wait until swift for loop with asynchronous network requests finishes executing
up vote
110
down vote
favorite
I would like a for in loop to send off a bunch of network requests to firebase, then pass the data to a new view controller once the the method finishes executing. Here is my code:
var datesArray = [String: AnyObject]()
for key in locationsArray {
let ref = Firebase(url: "http://myfirebase.com/" + "(key.0)")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
datesArray["(key.0)"] = snapshot.value
})
}
//Segue to new view controller here, and pass datesArray once it is complete
I have a couple concerns. First, how do I wait until the for loop is finished and all the network requests are complete? I can't modify the observeSingleEventOfType function, it is part of the firebase SDK. Also, will I create some sort of race condition by trying to access the datesArray from different iterations of the for loop (hope that makes sense)? I've been reading about GCD and NSOperation but I'm a bit lost as this is the first app I've built.
Note: Locations array is an array containing the keys I need to access in firebase. Also, it's important that the network requests are fired off asynchronously. I just want to wait until ALL the asynchronous requests complete before I pass the datesArray to the next view controller.
swift asynchronous grand-central-dispatch nsoperation
add a comment |
up vote
110
down vote
favorite
I would like a for in loop to send off a bunch of network requests to firebase, then pass the data to a new view controller once the the method finishes executing. Here is my code:
var datesArray = [String: AnyObject]()
for key in locationsArray {
let ref = Firebase(url: "http://myfirebase.com/" + "(key.0)")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
datesArray["(key.0)"] = snapshot.value
})
}
//Segue to new view controller here, and pass datesArray once it is complete
I have a couple concerns. First, how do I wait until the for loop is finished and all the network requests are complete? I can't modify the observeSingleEventOfType function, it is part of the firebase SDK. Also, will I create some sort of race condition by trying to access the datesArray from different iterations of the for loop (hope that makes sense)? I've been reading about GCD and NSOperation but I'm a bit lost as this is the first app I've built.
Note: Locations array is an array containing the keys I need to access in firebase. Also, it's important that the network requests are fired off asynchronously. I just want to wait until ALL the asynchronous requests complete before I pass the datesArray to the next view controller.
swift asynchronous grand-central-dispatch nsoperation
add a comment |
up vote
110
down vote
favorite
up vote
110
down vote
favorite
I would like a for in loop to send off a bunch of network requests to firebase, then pass the data to a new view controller once the the method finishes executing. Here is my code:
var datesArray = [String: AnyObject]()
for key in locationsArray {
let ref = Firebase(url: "http://myfirebase.com/" + "(key.0)")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
datesArray["(key.0)"] = snapshot.value
})
}
//Segue to new view controller here, and pass datesArray once it is complete
I have a couple concerns. First, how do I wait until the for loop is finished and all the network requests are complete? I can't modify the observeSingleEventOfType function, it is part of the firebase SDK. Also, will I create some sort of race condition by trying to access the datesArray from different iterations of the for loop (hope that makes sense)? I've been reading about GCD and NSOperation but I'm a bit lost as this is the first app I've built.
Note: Locations array is an array containing the keys I need to access in firebase. Also, it's important that the network requests are fired off asynchronously. I just want to wait until ALL the asynchronous requests complete before I pass the datesArray to the next view controller.
swift asynchronous grand-central-dispatch nsoperation
I would like a for in loop to send off a bunch of network requests to firebase, then pass the data to a new view controller once the the method finishes executing. Here is my code:
var datesArray = [String: AnyObject]()
for key in locationsArray {
let ref = Firebase(url: "http://myfirebase.com/" + "(key.0)")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
datesArray["(key.0)"] = snapshot.value
})
}
//Segue to new view controller here, and pass datesArray once it is complete
I have a couple concerns. First, how do I wait until the for loop is finished and all the network requests are complete? I can't modify the observeSingleEventOfType function, it is part of the firebase SDK. Also, will I create some sort of race condition by trying to access the datesArray from different iterations of the for loop (hope that makes sense)? I've been reading about GCD and NSOperation but I'm a bit lost as this is the first app I've built.
Note: Locations array is an array containing the keys I need to access in firebase. Also, it's important that the network requests are fired off asynchronously. I just want to wait until ALL the asynchronous requests complete before I pass the datesArray to the next view controller.
swift asynchronous grand-central-dispatch nsoperation
swift asynchronous grand-central-dispatch nsoperation
asked Mar 10 '16 at 2:37
Josh
7572715
7572715
add a comment |
add a comment |
7 Answers
7
active
oldest
votes
up vote
222
down vote
accepted
You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example in Swift 4.1 (works in Swift 3 too) 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.
For those using the older Swift 2.3, here's an example using its syntax:
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = dispatch_group_create()
for i in 0 ..< 5 {
dispatch_group_enter(myGroup)
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request (i)")
dispatch_group_leave(self.myGroup)
}
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
print("Finished all requests.")
})
}
This worked great! Thanks! Do you have any idea if I will run into any race conditions when I am trying to update the datesArray?
– Josh
Mar 10 '16 at 4:13
1
@Josh Regarding race condition: a race condition occurs, if the same memory location will be accessed from different threads, where at least one access is a write - without using synchronisation. All accesses within the same serial dispatch queue are synchronised, though. Synchronisation also occurs with memory operations occurring on dispatch queue A, which submits to another dispatch queue B. All operations in queue A are then synchronised in queue B. So, if you look at the solution, it's not automatically guaranteed that accesses are synchronised. ;)
– CouchDeveloper
Mar 28 '16 at 9:44
1
As of 20th december 2017 this no longer works. Can you please update your answer?
– tomSurge
Dec 19 '16 at 23:15
5
@tomSurge, December 2017 is 11 months away ;)
– SMPLGRP
Jan 2 '17 at 18:53
2
This answer works for me in May 2018 :)
– Lance Samaria
May 20 at 19:27
|
show 8 more comments
up vote
35
down vote
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.")
})
}
1
Hi, does this work for let´s say 100 requests? or 1000? Because I´m trying to do this with about 100 requests and is crashing on the completion of the request.
– lopes710
Feb 16 '17 at 0:18
I second @lopes710-- This appears to allow all requests to operate in parallel, right?
– Chris Prince
Jun 11 '17 at 15:45
if i have 2 network requests, one nested with the other, inside a for loop, then how to make sure that for each iteration of for loop, both requests have been completed. ?
– Awais Fayyaz
Aug 31 at 12:46
add a comment |
up vote
10
down vote
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()
}
}
}
add a comment |
up vote
9
down vote
Details
Xcode 9.2, Swift 4
Solution
class AsyncOperation {
typealias NumberOfPendingActions = Int
typealias DispatchQueueOfReturningValue = DispatchQueue
typealias CompleteClosure = ()->()
private let dispatchQueue: DispatchQueue
private var semaphore: DispatchSemaphore
private var numberOfPendingActionsQueue: DispatchQueue
public private(set) var numberOfPendingActions = 0
var whenCompleteAll: (()->())?
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
numberOfPendingActionsQueue = DispatchQueue(label: dispatchQueueLabel + "_numberOfPendingActionsQueue")
}
func run(closure: @escaping (@escaping CompleteClosure)->()) {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions += 1
}
dispatchQueue.async {
self.semaphore.wait()
closure {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions -= 1
if self.numberOfPendingActions == 0 {
self.whenCompleteAll?()
}
}
self.semaphore.signal()
}
}
}
}
Usage
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
print("(i)")
asyncOperation.run{ completeClosure in
// add any (sync/async) code
//..
// Make signal that this closure finished
completeClosure()
}
}
Full sample
import UIKit
class ViewController: UIViewController {
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 3, dispatchQueueLabel: "AnyString")
let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 40))
let label = UILabel(frame: CGRect(x: 180, y: 50, width: 150, height: 100))
var counter = 1
var labelCounter = 0
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
label.text = "(labelCounter)"
label.numberOfLines = 2
label.textAlignment = .natural
view.addSubview(button)
view.addSubview(label)
}
@objc func buttonTapped() {
//sample1()
sample2()
}
func sample1() {
print("Sample 1")
labelCounter += 1
label.text = "button tapped (labelCounter) times"
print("Button tapped at: (Date())")
asyncOperation.whenCompleteAll = {
print("All Done")
}
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(1)
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
func sample2() {
print("Sample 2")
label.text = ""
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(UInt32(i+i))
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
}
}
Results
Sample 1
Sample 2
add a comment |
up vote
5
down vote
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.
add a comment |
up vote
3
down vote
Swift 3:
You could also use semaphores on this way. It results very helpful, besides you can keep exact track on when and what processes are completed. This has been extracted from my code:
//You have to create your own queue or if you need the Default queue
let persons = persistentContainer.viewContext.persons
print("How many persons on database: (persons.count())")
let numberOfPersons = persons.count()
for eachPerson in persons{
queuePersonDetail.async {
self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
print("Person detail: (person2?.fullName)")
//When we get the completionHandler we send the signal
semaphorePersonDetailAndSave.signal()
}
}
}
//Here we will wait
for i in 0..<numberOfPersons{
semaphorePersonDetailAndSave.wait()
NSLog("(i + 1)/(persons.count()) completed")
}
//And here the flow continues...
It's very nice code for sure. Good one.
– Fattie
Dec 18 '16 at 15:59
add a comment |
up vote
0
down vote
Dispatch group is good but the order of sent requests is random.
Finished request 1
Finished request 0
Finished request 2
In my project case, each requests needed to be launch is the right order. If this could help someone :
public class RequestItem: NSObject {
public var urlToCall: String = ""
public var method: HTTPMethod = .get
public var params: [String: String] = [:]
public var headers: [String: String] = [:]
}
public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {
// If there is requests
if !requestItemsToSend.isEmpty {
let requestItemsToSendCopy = requestItemsToSend
NSLog("Send list started")
launchRequestsInOrder(requestItemsToSendCopy, 0, , launchRequestsInOrderCompletionBlock: { index, errors in
trySendRequestsNotSentCompletionHandler(errors)
})
}
else {
trySendRequestsNotSentCompletionHandler()
}
}
private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {
executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
if currentIndex < requestItemsToSend.count {
// We didn't reach last request, launch next request
self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in
launchRequestsInOrderCompletionBlock(currentIndex, errors)
})
}
else {
// We parse and send all requests
NSLog("Send list finished")
launchRequestsInOrderCompletionBlock(currentIndex, errors)
}
})
}
private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
NSLog("Send request %d", index)
Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in
var errors: [Error] = errors
switch response.result {
case .success:
// Request sended successfully, we can remove it from not sended request array
self.requestItemsToSend.remove(at: index)
break
case .failure:
// Still not send we append arror
errors.append(response.result.error!)
break
}
NSLog("Receive request %d", index)
executeRequestCompletionBlock(index+1, errors)
}
}
Call :
trySendRequestsNotSent()
Result :
Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished
See for more infos :
Gist
add a comment |
7 Answers
7
active
oldest
votes
7 Answers
7
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
222
down vote
accepted
You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example in Swift 4.1 (works in Swift 3 too) 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.
For those using the older Swift 2.3, here's an example using its syntax:
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = dispatch_group_create()
for i in 0 ..< 5 {
dispatch_group_enter(myGroup)
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request (i)")
dispatch_group_leave(self.myGroup)
}
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
print("Finished all requests.")
})
}
This worked great! Thanks! Do you have any idea if I will run into any race conditions when I am trying to update the datesArray?
– Josh
Mar 10 '16 at 4:13
1
@Josh Regarding race condition: a race condition occurs, if the same memory location will be accessed from different threads, where at least one access is a write - without using synchronisation. All accesses within the same serial dispatch queue are synchronised, though. Synchronisation also occurs with memory operations occurring on dispatch queue A, which submits to another dispatch queue B. All operations in queue A are then synchronised in queue B. So, if you look at the solution, it's not automatically guaranteed that accesses are synchronised. ;)
– CouchDeveloper
Mar 28 '16 at 9:44
1
As of 20th december 2017 this no longer works. Can you please update your answer?
– tomSurge
Dec 19 '16 at 23:15
5
@tomSurge, December 2017 is 11 months away ;)
– SMPLGRP
Jan 2 '17 at 18:53
2
This answer works for me in May 2018 :)
– Lance Samaria
May 20 at 19:27
|
show 8 more comments
up vote
222
down vote
accepted
You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example in Swift 4.1 (works in Swift 3 too) 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.
For those using the older Swift 2.3, here's an example using its syntax:
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = dispatch_group_create()
for i in 0 ..< 5 {
dispatch_group_enter(myGroup)
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request (i)")
dispatch_group_leave(self.myGroup)
}
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
print("Finished all requests.")
})
}
This worked great! Thanks! Do you have any idea if I will run into any race conditions when I am trying to update the datesArray?
– Josh
Mar 10 '16 at 4:13
1
@Josh Regarding race condition: a race condition occurs, if the same memory location will be accessed from different threads, where at least one access is a write - without using synchronisation. All accesses within the same serial dispatch queue are synchronised, though. Synchronisation also occurs with memory operations occurring on dispatch queue A, which submits to another dispatch queue B. All operations in queue A are then synchronised in queue B. So, if you look at the solution, it's not automatically guaranteed that accesses are synchronised. ;)
– CouchDeveloper
Mar 28 '16 at 9:44
1
As of 20th december 2017 this no longer works. Can you please update your answer?
– tomSurge
Dec 19 '16 at 23:15
5
@tomSurge, December 2017 is 11 months away ;)
– SMPLGRP
Jan 2 '17 at 18:53
2
This answer works for me in May 2018 :)
– Lance Samaria
May 20 at 19:27
|
show 8 more comments
up vote
222
down vote
accepted
up vote
222
down vote
accepted
You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example in Swift 4.1 (works in Swift 3 too) 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.
For those using the older Swift 2.3, here's an example using its syntax:
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = dispatch_group_create()
for i in 0 ..< 5 {
dispatch_group_enter(myGroup)
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request (i)")
dispatch_group_leave(self.myGroup)
}
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
print("Finished all requests.")
})
}
You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example in Swift 4.1 (works in Swift 3 too) 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.
For those using the older Swift 2.3, here's an example using its syntax:
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = dispatch_group_create()
for i in 0 ..< 5 {
dispatch_group_enter(myGroup)
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request (i)")
dispatch_group_leave(self.myGroup)
}
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
print("Finished all requests.")
})
}
edited Jun 14 at 12:40
answered Mar 10 '16 at 2:53
paulvs
7,35832847
7,35832847
This worked great! Thanks! Do you have any idea if I will run into any race conditions when I am trying to update the datesArray?
– Josh
Mar 10 '16 at 4:13
1
@Josh Regarding race condition: a race condition occurs, if the same memory location will be accessed from different threads, where at least one access is a write - without using synchronisation. All accesses within the same serial dispatch queue are synchronised, though. Synchronisation also occurs with memory operations occurring on dispatch queue A, which submits to another dispatch queue B. All operations in queue A are then synchronised in queue B. So, if you look at the solution, it's not automatically guaranteed that accesses are synchronised. ;)
– CouchDeveloper
Mar 28 '16 at 9:44
1
As of 20th december 2017 this no longer works. Can you please update your answer?
– tomSurge
Dec 19 '16 at 23:15
5
@tomSurge, December 2017 is 11 months away ;)
– SMPLGRP
Jan 2 '17 at 18:53
2
This answer works for me in May 2018 :)
– Lance Samaria
May 20 at 19:27
|
show 8 more comments
This worked great! Thanks! Do you have any idea if I will run into any race conditions when I am trying to update the datesArray?
– Josh
Mar 10 '16 at 4:13
1
@Josh Regarding race condition: a race condition occurs, if the same memory location will be accessed from different threads, where at least one access is a write - without using synchronisation. All accesses within the same serial dispatch queue are synchronised, though. Synchronisation also occurs with memory operations occurring on dispatch queue A, which submits to another dispatch queue B. All operations in queue A are then synchronised in queue B. So, if you look at the solution, it's not automatically guaranteed that accesses are synchronised. ;)
– CouchDeveloper
Mar 28 '16 at 9:44
1
As of 20th december 2017 this no longer works. Can you please update your answer?
– tomSurge
Dec 19 '16 at 23:15
5
@tomSurge, December 2017 is 11 months away ;)
– SMPLGRP
Jan 2 '17 at 18:53
2
This answer works for me in May 2018 :)
– Lance Samaria
May 20 at 19:27
This worked great! Thanks! Do you have any idea if I will run into any race conditions when I am trying to update the datesArray?
– Josh
Mar 10 '16 at 4:13
This worked great! Thanks! Do you have any idea if I will run into any race conditions when I am trying to update the datesArray?
– Josh
Mar 10 '16 at 4:13
1
1
@Josh Regarding race condition: a race condition occurs, if the same memory location will be accessed from different threads, where at least one access is a write - without using synchronisation. All accesses within the same serial dispatch queue are synchronised, though. Synchronisation also occurs with memory operations occurring on dispatch queue A, which submits to another dispatch queue B. All operations in queue A are then synchronised in queue B. So, if you look at the solution, it's not automatically guaranteed that accesses are synchronised. ;)
– CouchDeveloper
Mar 28 '16 at 9:44
@Josh Regarding race condition: a race condition occurs, if the same memory location will be accessed from different threads, where at least one access is a write - without using synchronisation. All accesses within the same serial dispatch queue are synchronised, though. Synchronisation also occurs with memory operations occurring on dispatch queue A, which submits to another dispatch queue B. All operations in queue A are then synchronised in queue B. So, if you look at the solution, it's not automatically guaranteed that accesses are synchronised. ;)
– CouchDeveloper
Mar 28 '16 at 9:44
1
1
As of 20th december 2017 this no longer works. Can you please update your answer?
– tomSurge
Dec 19 '16 at 23:15
As of 20th december 2017 this no longer works. Can you please update your answer?
– tomSurge
Dec 19 '16 at 23:15
5
5
@tomSurge, December 2017 is 11 months away ;)
– SMPLGRP
Jan 2 '17 at 18:53
@tomSurge, December 2017 is 11 months away ;)
– SMPLGRP
Jan 2 '17 at 18:53
2
2
This answer works for me in May 2018 :)
– Lance Samaria
May 20 at 19:27
This answer works for me in May 2018 :)
– Lance Samaria
May 20 at 19:27
|
show 8 more comments
up vote
35
down vote
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.")
})
}
1
Hi, does this work for let´s say 100 requests? or 1000? Because I´m trying to do this with about 100 requests and is crashing on the completion of the request.
– lopes710
Feb 16 '17 at 0:18
I second @lopes710-- This appears to allow all requests to operate in parallel, right?
– Chris Prince
Jun 11 '17 at 15:45
if i have 2 network requests, one nested with the other, inside a for loop, then how to make sure that for each iteration of for loop, both requests have been completed. ?
– Awais Fayyaz
Aug 31 at 12:46
add a comment |
up vote
35
down vote
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.")
})
}
1
Hi, does this work for let´s say 100 requests? or 1000? Because I´m trying to do this with about 100 requests and is crashing on the completion of the request.
– lopes710
Feb 16 '17 at 0:18
I second @lopes710-- This appears to allow all requests to operate in parallel, right?
– Chris Prince
Jun 11 '17 at 15:45
if i have 2 network requests, one nested with the other, inside a for loop, then how to make sure that for each iteration of for loop, both requests have been completed. ?
– Awais Fayyaz
Aug 31 at 12:46
add a comment |
up vote
35
down vote
up vote
35
down vote
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.")
})
}
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.")
})
}
edited Nov 2 '17 at 11:16
Adam Bardon
2,37462649
2,37462649
answered Jan 18 '17 at 17:58
Channel
1,1251013
1,1251013
1
Hi, does this work for let´s say 100 requests? or 1000? Because I´m trying to do this with about 100 requests and is crashing on the completion of the request.
– lopes710
Feb 16 '17 at 0:18
I second @lopes710-- This appears to allow all requests to operate in parallel, right?
– Chris Prince
Jun 11 '17 at 15:45
if i have 2 network requests, one nested with the other, inside a for loop, then how to make sure that for each iteration of for loop, both requests have been completed. ?
– Awais Fayyaz
Aug 31 at 12:46
add a comment |
1
Hi, does this work for let´s say 100 requests? or 1000? Because I´m trying to do this with about 100 requests and is crashing on the completion of the request.
– lopes710
Feb 16 '17 at 0:18
I second @lopes710-- This appears to allow all requests to operate in parallel, right?
– Chris Prince
Jun 11 '17 at 15:45
if i have 2 network requests, one nested with the other, inside a for loop, then how to make sure that for each iteration of for loop, both requests have been completed. ?
– Awais Fayyaz
Aug 31 at 12:46
1
1
Hi, does this work for let´s say 100 requests? or 1000? Because I´m trying to do this with about 100 requests and is crashing on the completion of the request.
– lopes710
Feb 16 '17 at 0:18
Hi, does this work for let´s say 100 requests? or 1000? Because I´m trying to do this with about 100 requests and is crashing on the completion of the request.
– lopes710
Feb 16 '17 at 0:18
I second @lopes710-- This appears to allow all requests to operate in parallel, right?
– Chris Prince
Jun 11 '17 at 15:45
I second @lopes710-- This appears to allow all requests to operate in parallel, right?
– Chris Prince
Jun 11 '17 at 15:45
if i have 2 network requests, one nested with the other, inside a for loop, then how to make sure that for each iteration of for loop, both requests have been completed. ?
– Awais Fayyaz
Aug 31 at 12:46
if i have 2 network requests, one nested with the other, inside a for loop, then how to make sure that for each iteration of for loop, both requests have been completed. ?
– Awais Fayyaz
Aug 31 at 12:46
add a comment |
up vote
10
down vote
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()
}
}
}
add a comment |
up vote
10
down vote
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()
}
}
}
add a comment |
up vote
10
down vote
up vote
10
down vote
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()
}
}
}
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()
}
}
}
edited Nov 7 at 6:10
answered Oct 20 '17 at 15:17
Timeless
3,22264075
3,22264075
add a comment |
add a comment |
up vote
9
down vote
Details
Xcode 9.2, Swift 4
Solution
class AsyncOperation {
typealias NumberOfPendingActions = Int
typealias DispatchQueueOfReturningValue = DispatchQueue
typealias CompleteClosure = ()->()
private let dispatchQueue: DispatchQueue
private var semaphore: DispatchSemaphore
private var numberOfPendingActionsQueue: DispatchQueue
public private(set) var numberOfPendingActions = 0
var whenCompleteAll: (()->())?
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
numberOfPendingActionsQueue = DispatchQueue(label: dispatchQueueLabel + "_numberOfPendingActionsQueue")
}
func run(closure: @escaping (@escaping CompleteClosure)->()) {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions += 1
}
dispatchQueue.async {
self.semaphore.wait()
closure {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions -= 1
if self.numberOfPendingActions == 0 {
self.whenCompleteAll?()
}
}
self.semaphore.signal()
}
}
}
}
Usage
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
print("(i)")
asyncOperation.run{ completeClosure in
// add any (sync/async) code
//..
// Make signal that this closure finished
completeClosure()
}
}
Full sample
import UIKit
class ViewController: UIViewController {
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 3, dispatchQueueLabel: "AnyString")
let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 40))
let label = UILabel(frame: CGRect(x: 180, y: 50, width: 150, height: 100))
var counter = 1
var labelCounter = 0
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
label.text = "(labelCounter)"
label.numberOfLines = 2
label.textAlignment = .natural
view.addSubview(button)
view.addSubview(label)
}
@objc func buttonTapped() {
//sample1()
sample2()
}
func sample1() {
print("Sample 1")
labelCounter += 1
label.text = "button tapped (labelCounter) times"
print("Button tapped at: (Date())")
asyncOperation.whenCompleteAll = {
print("All Done")
}
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(1)
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
func sample2() {
print("Sample 2")
label.text = ""
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(UInt32(i+i))
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
}
}
Results
Sample 1
Sample 2
add a comment |
up vote
9
down vote
Details
Xcode 9.2, Swift 4
Solution
class AsyncOperation {
typealias NumberOfPendingActions = Int
typealias DispatchQueueOfReturningValue = DispatchQueue
typealias CompleteClosure = ()->()
private let dispatchQueue: DispatchQueue
private var semaphore: DispatchSemaphore
private var numberOfPendingActionsQueue: DispatchQueue
public private(set) var numberOfPendingActions = 0
var whenCompleteAll: (()->())?
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
numberOfPendingActionsQueue = DispatchQueue(label: dispatchQueueLabel + "_numberOfPendingActionsQueue")
}
func run(closure: @escaping (@escaping CompleteClosure)->()) {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions += 1
}
dispatchQueue.async {
self.semaphore.wait()
closure {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions -= 1
if self.numberOfPendingActions == 0 {
self.whenCompleteAll?()
}
}
self.semaphore.signal()
}
}
}
}
Usage
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
print("(i)")
asyncOperation.run{ completeClosure in
// add any (sync/async) code
//..
// Make signal that this closure finished
completeClosure()
}
}
Full sample
import UIKit
class ViewController: UIViewController {
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 3, dispatchQueueLabel: "AnyString")
let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 40))
let label = UILabel(frame: CGRect(x: 180, y: 50, width: 150, height: 100))
var counter = 1
var labelCounter = 0
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
label.text = "(labelCounter)"
label.numberOfLines = 2
label.textAlignment = .natural
view.addSubview(button)
view.addSubview(label)
}
@objc func buttonTapped() {
//sample1()
sample2()
}
func sample1() {
print("Sample 1")
labelCounter += 1
label.text = "button tapped (labelCounter) times"
print("Button tapped at: (Date())")
asyncOperation.whenCompleteAll = {
print("All Done")
}
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(1)
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
func sample2() {
print("Sample 2")
label.text = ""
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(UInt32(i+i))
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
}
}
Results
Sample 1
Sample 2
add a comment |
up vote
9
down vote
up vote
9
down vote
Details
Xcode 9.2, Swift 4
Solution
class AsyncOperation {
typealias NumberOfPendingActions = Int
typealias DispatchQueueOfReturningValue = DispatchQueue
typealias CompleteClosure = ()->()
private let dispatchQueue: DispatchQueue
private var semaphore: DispatchSemaphore
private var numberOfPendingActionsQueue: DispatchQueue
public private(set) var numberOfPendingActions = 0
var whenCompleteAll: (()->())?
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
numberOfPendingActionsQueue = DispatchQueue(label: dispatchQueueLabel + "_numberOfPendingActionsQueue")
}
func run(closure: @escaping (@escaping CompleteClosure)->()) {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions += 1
}
dispatchQueue.async {
self.semaphore.wait()
closure {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions -= 1
if self.numberOfPendingActions == 0 {
self.whenCompleteAll?()
}
}
self.semaphore.signal()
}
}
}
}
Usage
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
print("(i)")
asyncOperation.run{ completeClosure in
// add any (sync/async) code
//..
// Make signal that this closure finished
completeClosure()
}
}
Full sample
import UIKit
class ViewController: UIViewController {
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 3, dispatchQueueLabel: "AnyString")
let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 40))
let label = UILabel(frame: CGRect(x: 180, y: 50, width: 150, height: 100))
var counter = 1
var labelCounter = 0
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
label.text = "(labelCounter)"
label.numberOfLines = 2
label.textAlignment = .natural
view.addSubview(button)
view.addSubview(label)
}
@objc func buttonTapped() {
//sample1()
sample2()
}
func sample1() {
print("Sample 1")
labelCounter += 1
label.text = "button tapped (labelCounter) times"
print("Button tapped at: (Date())")
asyncOperation.whenCompleteAll = {
print("All Done")
}
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(1)
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
func sample2() {
print("Sample 2")
label.text = ""
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(UInt32(i+i))
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
}
}
Results
Sample 1
Sample 2
Details
Xcode 9.2, Swift 4
Solution
class AsyncOperation {
typealias NumberOfPendingActions = Int
typealias DispatchQueueOfReturningValue = DispatchQueue
typealias CompleteClosure = ()->()
private let dispatchQueue: DispatchQueue
private var semaphore: DispatchSemaphore
private var numberOfPendingActionsQueue: DispatchQueue
public private(set) var numberOfPendingActions = 0
var whenCompleteAll: (()->())?
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
numberOfPendingActionsQueue = DispatchQueue(label: dispatchQueueLabel + "_numberOfPendingActionsQueue")
}
func run(closure: @escaping (@escaping CompleteClosure)->()) {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions += 1
}
dispatchQueue.async {
self.semaphore.wait()
closure {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions -= 1
if self.numberOfPendingActions == 0 {
self.whenCompleteAll?()
}
}
self.semaphore.signal()
}
}
}
}
Usage
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
print("(i)")
asyncOperation.run{ completeClosure in
// add any (sync/async) code
//..
// Make signal that this closure finished
completeClosure()
}
}
Full sample
import UIKit
class ViewController: UIViewController {
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 3, dispatchQueueLabel: "AnyString")
let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 40))
let label = UILabel(frame: CGRect(x: 180, y: 50, width: 150, height: 100))
var counter = 1
var labelCounter = 0
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
label.text = "(labelCounter)"
label.numberOfLines = 2
label.textAlignment = .natural
view.addSubview(button)
view.addSubview(label)
}
@objc func buttonTapped() {
//sample1()
sample2()
}
func sample1() {
print("Sample 1")
labelCounter += 1
label.text = "button tapped (labelCounter) times"
print("Button tapped at: (Date())")
asyncOperation.whenCompleteAll = {
print("All Done")
}
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(1)
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
func sample2() {
print("Sample 2")
label.text = ""
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action (counter) strat at (Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(UInt32(i+i))
print(" - Loading action (counter) end at (Date())")
completeClosure()
}
}
}
}
}
Results
Sample 1
Sample 2
edited Apr 11 at 7:59
answered Nov 16 '17 at 17:09
Vasily Bodnarchuk
8,19754857
8,19754857
add a comment |
add a comment |
up vote
5
down vote
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.
add a comment |
up vote
5
down vote
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.
add a comment |
up vote
5
down vote
up vote
5
down vote
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.
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.
answered Mar 10 '16 at 4:05
Shripada
3,89811625
3,89811625
add a comment |
add a comment |
up vote
3
down vote
Swift 3:
You could also use semaphores on this way. It results very helpful, besides you can keep exact track on when and what processes are completed. This has been extracted from my code:
//You have to create your own queue or if you need the Default queue
let persons = persistentContainer.viewContext.persons
print("How many persons on database: (persons.count())")
let numberOfPersons = persons.count()
for eachPerson in persons{
queuePersonDetail.async {
self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
print("Person detail: (person2?.fullName)")
//When we get the completionHandler we send the signal
semaphorePersonDetailAndSave.signal()
}
}
}
//Here we will wait
for i in 0..<numberOfPersons{
semaphorePersonDetailAndSave.wait()
NSLog("(i + 1)/(persons.count()) completed")
}
//And here the flow continues...
It's very nice code for sure. Good one.
– Fattie
Dec 18 '16 at 15:59
add a comment |
up vote
3
down vote
Swift 3:
You could also use semaphores on this way. It results very helpful, besides you can keep exact track on when and what processes are completed. This has been extracted from my code:
//You have to create your own queue or if you need the Default queue
let persons = persistentContainer.viewContext.persons
print("How many persons on database: (persons.count())")
let numberOfPersons = persons.count()
for eachPerson in persons{
queuePersonDetail.async {
self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
print("Person detail: (person2?.fullName)")
//When we get the completionHandler we send the signal
semaphorePersonDetailAndSave.signal()
}
}
}
//Here we will wait
for i in 0..<numberOfPersons{
semaphorePersonDetailAndSave.wait()
NSLog("(i + 1)/(persons.count()) completed")
}
//And here the flow continues...
It's very nice code for sure. Good one.
– Fattie
Dec 18 '16 at 15:59
add a comment |
up vote
3
down vote
up vote
3
down vote
Swift 3:
You could also use semaphores on this way. It results very helpful, besides you can keep exact track on when and what processes are completed. This has been extracted from my code:
//You have to create your own queue or if you need the Default queue
let persons = persistentContainer.viewContext.persons
print("How many persons on database: (persons.count())")
let numberOfPersons = persons.count()
for eachPerson in persons{
queuePersonDetail.async {
self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
print("Person detail: (person2?.fullName)")
//When we get the completionHandler we send the signal
semaphorePersonDetailAndSave.signal()
}
}
}
//Here we will wait
for i in 0..<numberOfPersons{
semaphorePersonDetailAndSave.wait()
NSLog("(i + 1)/(persons.count()) completed")
}
//And here the flow continues...
Swift 3:
You could also use semaphores on this way. It results very helpful, besides you can keep exact track on when and what processes are completed. This has been extracted from my code:
//You have to create your own queue or if you need the Default queue
let persons = persistentContainer.viewContext.persons
print("How many persons on database: (persons.count())")
let numberOfPersons = persons.count()
for eachPerson in persons{
queuePersonDetail.async {
self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
print("Person detail: (person2?.fullName)")
//When we get the completionHandler we send the signal
semaphorePersonDetailAndSave.signal()
}
}
}
//Here we will wait
for i in 0..<numberOfPersons{
semaphorePersonDetailAndSave.wait()
NSLog("(i + 1)/(persons.count()) completed")
}
//And here the flow continues...
answered Dec 1 '16 at 22:34
freaklix
713
713
It's very nice code for sure. Good one.
– Fattie
Dec 18 '16 at 15:59
add a comment |
It's very nice code for sure. Good one.
– Fattie
Dec 18 '16 at 15:59
It's very nice code for sure. Good one.
– Fattie
Dec 18 '16 at 15:59
It's very nice code for sure. Good one.
– Fattie
Dec 18 '16 at 15:59
add a comment |
up vote
0
down vote
Dispatch group is good but the order of sent requests is random.
Finished request 1
Finished request 0
Finished request 2
In my project case, each requests needed to be launch is the right order. If this could help someone :
public class RequestItem: NSObject {
public var urlToCall: String = ""
public var method: HTTPMethod = .get
public var params: [String: String] = [:]
public var headers: [String: String] = [:]
}
public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {
// If there is requests
if !requestItemsToSend.isEmpty {
let requestItemsToSendCopy = requestItemsToSend
NSLog("Send list started")
launchRequestsInOrder(requestItemsToSendCopy, 0, , launchRequestsInOrderCompletionBlock: { index, errors in
trySendRequestsNotSentCompletionHandler(errors)
})
}
else {
trySendRequestsNotSentCompletionHandler()
}
}
private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {
executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
if currentIndex < requestItemsToSend.count {
// We didn't reach last request, launch next request
self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in
launchRequestsInOrderCompletionBlock(currentIndex, errors)
})
}
else {
// We parse and send all requests
NSLog("Send list finished")
launchRequestsInOrderCompletionBlock(currentIndex, errors)
}
})
}
private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
NSLog("Send request %d", index)
Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in
var errors: [Error] = errors
switch response.result {
case .success:
// Request sended successfully, we can remove it from not sended request array
self.requestItemsToSend.remove(at: index)
break
case .failure:
// Still not send we append arror
errors.append(response.result.error!)
break
}
NSLog("Receive request %d", index)
executeRequestCompletionBlock(index+1, errors)
}
}
Call :
trySendRequestsNotSent()
Result :
Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished
See for more infos :
Gist
add a comment |
up vote
0
down vote
Dispatch group is good but the order of sent requests is random.
Finished request 1
Finished request 0
Finished request 2
In my project case, each requests needed to be launch is the right order. If this could help someone :
public class RequestItem: NSObject {
public var urlToCall: String = ""
public var method: HTTPMethod = .get
public var params: [String: String] = [:]
public var headers: [String: String] = [:]
}
public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {
// If there is requests
if !requestItemsToSend.isEmpty {
let requestItemsToSendCopy = requestItemsToSend
NSLog("Send list started")
launchRequestsInOrder(requestItemsToSendCopy, 0, , launchRequestsInOrderCompletionBlock: { index, errors in
trySendRequestsNotSentCompletionHandler(errors)
})
}
else {
trySendRequestsNotSentCompletionHandler()
}
}
private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {
executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
if currentIndex < requestItemsToSend.count {
// We didn't reach last request, launch next request
self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in
launchRequestsInOrderCompletionBlock(currentIndex, errors)
})
}
else {
// We parse and send all requests
NSLog("Send list finished")
launchRequestsInOrderCompletionBlock(currentIndex, errors)
}
})
}
private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
NSLog("Send request %d", index)
Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in
var errors: [Error] = errors
switch response.result {
case .success:
// Request sended successfully, we can remove it from not sended request array
self.requestItemsToSend.remove(at: index)
break
case .failure:
// Still not send we append arror
errors.append(response.result.error!)
break
}
NSLog("Receive request %d", index)
executeRequestCompletionBlock(index+1, errors)
}
}
Call :
trySendRequestsNotSent()
Result :
Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished
See for more infos :
Gist
add a comment |
up vote
0
down vote
up vote
0
down vote
Dispatch group is good but the order of sent requests is random.
Finished request 1
Finished request 0
Finished request 2
In my project case, each requests needed to be launch is the right order. If this could help someone :
public class RequestItem: NSObject {
public var urlToCall: String = ""
public var method: HTTPMethod = .get
public var params: [String: String] = [:]
public var headers: [String: String] = [:]
}
public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {
// If there is requests
if !requestItemsToSend.isEmpty {
let requestItemsToSendCopy = requestItemsToSend
NSLog("Send list started")
launchRequestsInOrder(requestItemsToSendCopy, 0, , launchRequestsInOrderCompletionBlock: { index, errors in
trySendRequestsNotSentCompletionHandler(errors)
})
}
else {
trySendRequestsNotSentCompletionHandler()
}
}
private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {
executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
if currentIndex < requestItemsToSend.count {
// We didn't reach last request, launch next request
self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in
launchRequestsInOrderCompletionBlock(currentIndex, errors)
})
}
else {
// We parse and send all requests
NSLog("Send list finished")
launchRequestsInOrderCompletionBlock(currentIndex, errors)
}
})
}
private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
NSLog("Send request %d", index)
Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in
var errors: [Error] = errors
switch response.result {
case .success:
// Request sended successfully, we can remove it from not sended request array
self.requestItemsToSend.remove(at: index)
break
case .failure:
// Still not send we append arror
errors.append(response.result.error!)
break
}
NSLog("Receive request %d", index)
executeRequestCompletionBlock(index+1, errors)
}
}
Call :
trySendRequestsNotSent()
Result :
Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished
See for more infos :
Gist
Dispatch group is good but the order of sent requests is random.
Finished request 1
Finished request 0
Finished request 2
In my project case, each requests needed to be launch is the right order. If this could help someone :
public class RequestItem: NSObject {
public var urlToCall: String = ""
public var method: HTTPMethod = .get
public var params: [String: String] = [:]
public var headers: [String: String] = [:]
}
public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {
// If there is requests
if !requestItemsToSend.isEmpty {
let requestItemsToSendCopy = requestItemsToSend
NSLog("Send list started")
launchRequestsInOrder(requestItemsToSendCopy, 0, , launchRequestsInOrderCompletionBlock: { index, errors in
trySendRequestsNotSentCompletionHandler(errors)
})
}
else {
trySendRequestsNotSentCompletionHandler()
}
}
private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {
executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
if currentIndex < requestItemsToSend.count {
// We didn't reach last request, launch next request
self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in
launchRequestsInOrderCompletionBlock(currentIndex, errors)
})
}
else {
// We parse and send all requests
NSLog("Send list finished")
launchRequestsInOrderCompletionBlock(currentIndex, errors)
}
})
}
private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
NSLog("Send request %d", index)
Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in
var errors: [Error] = errors
switch response.result {
case .success:
// Request sended successfully, we can remove it from not sended request array
self.requestItemsToSend.remove(at: index)
break
case .failure:
// Still not send we append arror
errors.append(response.result.error!)
break
}
NSLog("Receive request %d", index)
executeRequestCompletionBlock(index+1, errors)
}
}
Call :
trySendRequestsNotSent()
Result :
Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished
See for more infos :
Gist
answered Sep 1 '17 at 15:43
Aximem
429519
429519
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f35906568%2fwait-until-swift-for-loop-with-asynchronous-network-requests-finishes-executing%23new-answer', 'question_page');
}
);
Post as a guest
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password