Search code examples
iosswiftasynchronousalamofiregrand-central-dispatch

Error in handling a series of async functions using dispatchGroup in Swift


I have a project with 2 sets of async functions which should be executed in order. After all async functions are executed, I'm trying to run function3.

Here is a high level structure of my code:

class VC: UIViewController {
    let dispatchGroup = DispatchGroup()
    let dispatchGroup2 = DispatchGroup()
    override func viewDidLoad() {

        asyncFunc1()

        dispatchGroup.notify(queue: .main) {
            asyncFunc2()
        }

        dispatchGroup2.notify(queue: .main) {
            print("all done")
            function3()
        }
    }

    func asyncFunc1() {
        for item in itemArray {
            dispatchGroup.enter()
            Alamofire.request(urlString).responseString { response in
                dispatchGroup.leave()
            }
        }
    }

    func asyncFunc2() {
        for item in itemArray2 {
            dispatchGroup2.enter()
            Alamofire.request(urlString).responseString { response in
                dispatchGroup2.leave()
            }
        }
    }
}

My expectation is that functions run in order of asyncFunc1() -> asyncFunc2() -> function3(). When I actually run this app, I see that dispatchGroup2.notify is triggered at the beginning when program runs and never gets called after asyncFunc2 is done. What's wrong over here? I've tried doing with one dispatchGroup variable instead of having 2 (dispatchGroup and dispatchGroup2) but this didn't work again.


Solution

  • I would recommend a simple refactoring to encapsulate those DispatchGroups inside the corresponding asynchronous functions:

    func asyncFunc(completion: @escaping () -> Void) {
        let dispatchGroup = DispatchGroup()
        for ... {
            dispatchGroup.enter()
            request ... {
                dispatchGroup.leave()
            }
        }
        dispatchGroup.notify(queue: .main) { 
            completion() // Async function completed! 
        }
    }
    

    This would result in a very nice/compact top-level solution:

    asyncFunc1 {
        self.asyncFunc2 {
            print("all done")
            self.function3()
        }
    }
    

    Initial bug. Your original issue was caused by calling notify(queue:) before the corresponding enter() method. Your dispatch group was over even before it began ;)