Search code examples
swiftgrand-central-dispatch

Several tasks inside a DispatchGroup. Will they run in order?


In the following code, is it safe to append to an array? Is the order guaranteed to be maintained?

let processedData: [SomeType] = []
let dispatchGroup = DispatchGroup()
for _ in 0..<N {
    dispatchGroup.enter()
    startSomeAsyncTaskXYZ { (data, error) in
        // handle error and process data
        // add processed data to an array
        processedData.append(..)
        dispatchGroup.leave()
    }
}
dispatchGroup.notify(queue: .main) {
    // update UI
}

Solution

  • To stick with DispatchGroup while preserving the desired asynchronous nature and the expected ordering, make your array an array of optionals and populate it in whatever order the tasks complete:

    var processedData: [SomeType?] = Array(repeating: nil, count: N)
    let dispatchGroup = DispatchGroup()
    for idx in 0..<N {
        dispatchGroup.enter()
        startSomeAsyncTaskXYZ { (data, error) in
            // Ensure we always .leave() after we're done
            // handling the completion of the task
            defer { dispatchGroup.leave() }
    
            guard let data = data,
                  error == nil else {
                // TODO: Actual error handling
                return
            }
    
            // This needs to be .sync now (not .async) to ensure
            // the deferred dispatchGroup.leave() is not called
            // until *after* we've updated the array
            DispatchQueue.main.sync {
                processedData[idx] = SomeType(data: data)
            }
        }
    }
    dispatchGroup.notify(queue: .main) {
        // update UI
    }