8
\$\begingroup\$

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:

  1. A general code review
  2. 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!)
        }
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.