Wait until swift for loop with asynchronous network requests finishes executing











up vote
110
down vote

favorite
68












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.










share|improve this question


























    up vote
    110
    down vote

    favorite
    68












    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.










    share|improve this question
























      up vote
      110
      down vote

      favorite
      68









      up vote
      110
      down vote

      favorite
      68






      68





      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.










      share|improve this question













      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Mar 10 '16 at 2:37









      Josh

      7572715




      7572715
























          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.")
          })
          }





          share|improve this answer























          • 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




















          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.")
          })
          }





          share|improve this answer



















          • 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


















          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()
          }
          }
          }





          share|improve this answer






























            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



            enter image description here



            Sample 2



            enter image description here






            share|improve this answer






























              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.





              share|improve this answer




























                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...





                share|improve this answer





















                • It's very nice code for sure. Good one.
                  – Fattie
                  Dec 18 '16 at 15:59


















                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






                share|improve this answer





















                  Your Answer






                  StackExchange.ifUsing("editor", function () {
                  StackExchange.using("externalEditor", function () {
                  StackExchange.using("snippets", function () {
                  StackExchange.snippets.init();
                  });
                  });
                  }, "code-snippets");

                  StackExchange.ready(function() {
                  var channelOptions = {
                  tags: "".split(" "),
                  id: "1"
                  };
                  initTagRenderer("".split(" "), "".split(" "), channelOptions);

                  StackExchange.using("externalEditor", function() {
                  // Have to fire editor after snippets, if snippets enabled
                  if (StackExchange.settings.snippets.snippetsEnabled) {
                  StackExchange.using("snippets", function() {
                  createEditor();
                  });
                  }
                  else {
                  createEditor();
                  }
                  });

                  function createEditor() {
                  StackExchange.prepareEditor({
                  heartbeatType: 'answer',
                  convertImagesToLinks: true,
                  noModals: true,
                  showLowRepImageUploadWarning: true,
                  reputationToPostImages: 10,
                  bindNavPrevention: true,
                  postfix: "",
                  imageUploader: {
                  brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                  contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                  allowUrls: true
                  },
                  onDemand: true,
                  discardSelector: ".discard-answer"
                  ,immediatelyShowMarkdownHelp:true
                  });


                  }
                  });














                   

                  draft saved


                  draft discarded


















                  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
































                  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.")
                  })
                  }





                  share|improve this answer























                  • 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

















                  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.")
                  })
                  }





                  share|improve this answer























                  • 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















                  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.")
                  })
                  }





                  share|improve this answer














                  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.")
                  })
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  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




















                  • 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














                  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.")
                  })
                  }





                  share|improve this answer



















                  • 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















                  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.")
                  })
                  }





                  share|improve this answer



















                  • 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













                  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.")
                  })
                  }





                  share|improve this answer














                  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.")
                  })
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  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














                  • 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










                  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()
                  }
                  }
                  }





                  share|improve this answer



























                    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()
                    }
                    }
                    }





                    share|improve this answer

























                      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()
                      }
                      }
                      }





                      share|improve this answer














                      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()
                      }
                      }
                      }






                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Nov 7 at 6:10

























                      answered Oct 20 '17 at 15:17









                      Timeless

                      3,22264075




                      3,22264075






















                          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



                          enter image description here



                          Sample 2



                          enter image description here






                          share|improve this answer



























                            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



                            enter image description here



                            Sample 2



                            enter image description here






                            share|improve this answer

























                              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



                              enter image description here



                              Sample 2



                              enter image description here






                              share|improve this answer














                              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



                              enter image description here



                              Sample 2



                              enter image description here







                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited Apr 11 at 7:59

























                              answered Nov 16 '17 at 17:09









                              Vasily Bodnarchuk

                              8,19754857




                              8,19754857






















                                  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.





                                  share|improve this answer

























                                    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.





                                    share|improve this answer























                                      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.





                                      share|improve this answer












                                      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.






                                      share|improve this answer












                                      share|improve this answer



                                      share|improve this answer










                                      answered Mar 10 '16 at 4:05









                                      Shripada

                                      3,89811625




                                      3,89811625






















                                          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...





                                          share|improve this answer





















                                          • It's very nice code for sure. Good one.
                                            – Fattie
                                            Dec 18 '16 at 15:59















                                          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...





                                          share|improve this answer





















                                          • It's very nice code for sure. Good one.
                                            – Fattie
                                            Dec 18 '16 at 15:59













                                          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...





                                          share|improve this answer












                                          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...






                                          share|improve this answer












                                          share|improve this answer



                                          share|improve this answer










                                          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


















                                          • 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










                                          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






                                          share|improve this answer

























                                            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






                                            share|improve this answer























                                              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






                                              share|improve this answer












                                              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







                                              share|improve this answer












                                              share|improve this answer



                                              share|improve this answer










                                              answered Sep 1 '17 at 15:43









                                              Aximem

                                              429519




                                              429519






























                                                   

                                                  draft saved


                                                  draft discarded



















































                                                   


                                                  draft saved


                                                  draft discarded














                                                  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




















































































                                                  Popular posts from this blog

                                                  横浜市

                                                  Rostock

                                                  Europa