iosswiftasynchronousfor-in-loopdispatchgroup

How to use DispatchGroup to update UI only after for-in loop has completed


When a user taps a button, that triggers a lengthy function consisting of an API call and calculations.

@IBAction func buttonTapped(_ sender: Any) {
        
        var itemIndex = 0
        let dispatchGroup = DispatchGroup()
        
        for name in (entireRecipe?.extendedIngredients!)! {
            
            dispatchGroup.enter()
            
            CalculatorService().calculate() { item   in
                self.arrayItem.append(item)
                self.totalItems = self.arrayItems.reduce(0, +)
            }
            itemIndex = itemIndex + 1
        }
        dispatchGroup.leave()
        
        dispatchGroup.notify(queue: .main) {
            print("DispatchGroup Notified")
            self.itemLabel.text = "\(self.totalItems)"
        }
    }

My rationale on the above is that:

  1. dispatchGroup is entered when the for-in loop starts
  2. dispatchGroup is left after the for-in loop has iterated through the entire array
  3. dispatchGroup is only then notified, and the UI label is updated

However, it looks like dispatchGroup.notify is not being run when intended, and so the UI label is not being updated after the for-in loop is complete.

Is there anything missing from my dispatchGroup code? Any help is much appreciated!


Solution

  • The leave line must be inside the closure

    for name in (entireRecipe?.extendedIngredients!)! {
        
        dispatchGroup.enter()
        
        CalculatorService().calculate() { item   in
            self.arrayItem.append(item)
            self.totalItems = self.arrayItems.reduce(0, +)
            dispatchGroup.leave()
        }
        itemIndex = itemIndex + 1
    }