Signalling notify only after all sync requests have completed
<p>Getting a notification when all async requests have completed. </p>

<p>I call <code>registerTrack</code> multiple times in the loop below. I want to only trigger the <code>syncGroup.notify</code> when <strong><em>all</em></strong> requests have been completed. From looking at this code, its going to <code>notify</code> after each successful completion of <code>registerTrack</code>. How do I make it signal <code>notify</code> only after all <code>registerTrack</code> operations have been completed?</p>

<p>My code is as follows:</p>

<pre><code>    var fail = false
    let syncGroup = DispatchGroup() //used so we can determine if all the register requests have finished

    for track in unsyncedTracks {
        syncGroup.enter()

        if fail {
            syncGroup.leave()
            break //might save an aync request from getting executed on failure
        }

        registerTrack(track: track, withCompletion: { (success: Bool) -&gt; () in

            if success {
                self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)")
                syncGroup.leave()
            }
            else {
                fail = true
                syncGroup.leave()
            }
        })
    }

    //all requests complete
    syncGroup.notify(queue: .main) {
        if fail {
            complete(false)
        }
        else {
            self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks")
            complete(true)
        }
    }
</code></pre>

<p>EDIT: </p>

<p>Based on the recommendation to use GCD concurrent queues, I changed my code to:</p>

<pre><code>    var failed = false
    //work item to loop through and call registerTrack on each track
    let registerTracksWorkItem = DispatchWorkItem {
        for track in unsyncedTracks {
            if failed { //exit early on failure, potentially save a network request
                break
            }

            self.registerTrack(track: track, withCompletion: { (success: Bool) -&gt; () in

                if success {
                    self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)")
                }
                else {
                    failed = true
                }
            })
        }
    }

    //handler for when all registerTrack calls are complete
    registerTracksWorkItem.notify(queue: DispatchQueue.main) {
        if failed {
            self.debug.log(tag: "RecordViewController", content: "At least one registerTrack call failed")
            complete(false)
        }
        else {
            self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks")
            complete(true)
        }
    }

    //execute the work item
    let queue = DispatchQueue.global()
    queue.async(execute: registerTracksWorkItem)
</code></pre>

<p>This code does not wait for <code>registerTrack</code> to finish, rather it executes them all and calls the <code>notify</code> event. :(</p>

<p>EDIT 3:</p>

<p>So this works. It uses a counter to check to see if its at the final <code>unsyncedTracks</code> object.</p>

<pre><code>    var fail = false
    let syncGroup = DispatchGroup() //used so we can determine if all the register requests have finished

    //used to make sure notify is only triggered when all registerTracks are complete
    let unsyncedTracksCount = unsyncedTracks.count
    var completeCounter = 0

    syncGroup.enter()
    for track in unsyncedTracks {

        registerTrack(track: track, withCompletion: { (success: Bool) -&gt; () in

            if success {
                self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)")
            }
            else {
                fail = true
            }

            completeCounter = completeCounter + 1
            if completeCounter == unsyncedTracksCount {
                syncGroup.leave()
            }
        })
    }

    //all requests complete
    syncGroup.notify(queue: .main) {
        if fail {
            complete(false)
        }
        else {
            self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks")
            complete(true)
        }
    }
</code></pre>

<p>Can I do this (it also appears to work)? I added the if fail, <code>leave()</code> if. </p>

<pre><code>    var fail = false
    let syncGroup = DispatchGroup() //used so we can determine if all the register requests have finished

    //used to make sure notify is only triggered when all registerTracks are complete
    let unsyncedTracksCount = unsyncedTracks.count
    var completeCounter = 0

    syncGroup.enter()
    for track in unsyncedTracks {
        if fail { //might save an aync request from getting executed on failure
            syncGroup.leave()
            break
        }

        registerTrack(track: track, withCompletion: { (success: Bool) -&gt; () in

            if success {
                self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)")
            }
            else {
                fail = true
            }

            completeCounter = completeCounter + 1
            if completeCounter == unsyncedTracksCount {
                syncGroup.leave()
            }
        })
    }

    //all requests complete
    syncGroup.notify(queue: .main) {
        if fail {
            complete(false)
        }
        else {
            self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks")
            complete(true)
        }
    }
</code></pre>