18

I want to run a for loop in swift in order, DispatchGroup will fire them together, so I want to use DispatchQueue and DispatchSemaphore to achieve my goal. I failed to make my program work, how can I force them to wait and run one by one?

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let dispatchSemaphore = DispatchSemaphore(value: 1)

for c in self.categories {

    dispatchSemaphore.wait()

    dispatchQueue.async(group: dispatchGroup) {

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

dispatchGroup.notify(queue: dispatchQueue) {

    self.refreshOrderTable { _ in

        self.productCollectionView.reloadData()

        NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
    }
}

Thanks to Palle, here is my final code:

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let dispatchSemaphore = DispatchSemaphore(value: 0)

dispatchQueue.async {

    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()
        }
    }
}
12
  • 2
    Unrelated but don't make the weak self - strong self dance. It's nonsensical because GCD does not cause retain cycles. Commented Oct 21, 2017 at 13:07
  • If you want to execute in order, why use concurrency anyway? Just create a loop in a background queue and execute your downloadProductsByCategory function directly in the loop. Commented Oct 21, 2017 at 13:15
  • @Palle downloadProductsByCategory itself is async method using alamofire Commented Oct 21, 2017 at 13:16
  • Then just put dispatchSemaphore.wait() after the call to downloadProductsByCategory and dispatchSemaphore.signal() in the completion handler. Commented Oct 21, 2017 at 13:17
  • @Palle for some reason, when dispatchSemaphore.wait() called for the second time, the whole program will be blocked there forever. and downloadProductsByCategory never receive a callback Commented Oct 21, 2017 at 13:24

1 Answer 1

19

You can put the whole loop in a block instead of putting just the download function in a block:

dispatchQueue.async {
    for c in self.categories {
        if let id = c.categoryId {
            self.downloadProductsByCategory(categoryId: id) { success, data in
                if success, let products = data {
                    self.products.append(products)
                }

                dispatchSemaphore.signal()
            }
            dispatchSemaphore.wait()
        }
    }
}

You can simplify your code by using compactMap to unwrap your product ids:

for id in self.categories.compactMap({$0.categoryId}) { ... }
Sign up to request clarification or add additional context in comments.

6 Comments

thanks, it works. but one more thing, how can I get notified when I get all data? can I use DispatchGroup with the current code?
Just put a DispatchQueue.main.async { ... } in the block when you're done with the loop. A dispatch group with a single enter and leave will of course also work.
There really is NO need for a dispatch group when the code itself is synchronous. It doesn't make sense. Just call your completion block right after your for-loop.
THe dispatchSemaphore in this answer is not created anywhere.
The semaphore is initialized with a value of 0: let dispatchSemaphore = DispatchSemaphore(value: 0) before. You can see the full final solution in the question.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.