I put together an asynchronous map
function for Swift's Array
using Grand Central Dispatch. It's called with a transform closure, a dispatch_queue_t
(optional), and a completion closure. The transform closure is passed an item from the array and a callback closure which should be called with the result of the transform. When the completion closure is called, it will be passed a new array with the transformed items in the same order as the original array.
Simple example:
Array(1...10).map({
(num, callback) in
// Just to show the transform can perform an asynchronous action
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
callback(num * 2)
}
}) {
results in
println(results)
}
Outputs:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
By default, the transform closure is dispatched to the DISPATCH_QUEUE_PRIORITY_LOW
global queue for each item, but a different queue can be passed in if need be.
I'm looking for:
- A general code review
- Anything that could take more advantage of Swift or GCD
extension Array {
// Convenience function which passes the global DISPATCH_QUEUE_PRIORITY_LOW queue as the transformQueue
public func map<U>(transform: (T, U -> ()) -> (), withCompletionHandler completionHandler: [U] -> ()) {
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
map(transform, onQueue: queue, withCompletionHandler: completionHandler)
}
public func map<U>(transform: (T, U -> ()) -> (), onQueue tranformQueue: dispatch_queue_t, withCompletionHandler completionHandler: [U] -> ()) {
let transformGroup = dispatch_group_create()
let dataSyncQueue = dispatch_queue_create("array.asyncMap.dataSync", DISPATCH_QUEUE_SERIAL)
var results: [U?] = [U?](count: count, repeatedValue: nil)
// Nested function to dispatch the transform; mainly here to capture index from the for loop below
func performTransform(index: Int) {
let item = self[index]
dispatch_group_enter(transformGroup)
dispatch_async(tranformQueue) {
transform(item) {
result in
dispatch_sync(dataSyncQueue) {
results[index] = result
}
dispatch_group_leave(transformGroup)
}
}
}
for index in (0..<count) {
performTransform(index)
}
dispatch_group_notify(transformGroup, tranformQueue) {
var unwrappedResults: [U]? = nil
// This dispatch_sync doesn't seem to be technically needed since all the transforms should have finished at this point
dispatch_sync(dataSyncQueue) {
// Force unwrap the values in the results array
unwrappedResults = results.map({ item in item! })
}
completionHandler(unwrappedResults!)
}
}
}