iOS Concurrency with GCD and Operations - Part 3: | Ray Wenderlich

Learn how to wrap a synchronous function to make it asynchronous, and how to handle simple use cases — a chain of synchronous tasks and a collection of similar independent tasks.

This is a companion discussion topic for the original entry at

Maybe I’m missing something here, but it appears that the definition of asyncTiltShift in the Challenge playground is faulty – it never actually uses the passed-in completionQueue. Instead, it uses runQueue for both the background processing and calling the completion handler.

In accordance with the code shown in the video and the Demo playground, the function should read something like this:

func asyncTiltShift(_ inputImage: UIImage?, runQueue: DispatchQueue, completionQueue: DispatchQueue, completion: @escaping (UIImage?) -> ()) {
runQueue.async {
        let outputImage = tiltShift(image: inputImage)
        completionQueue.async {

you’re right! I’m hoping the fixed 02-Challenge playground will be available soon.

sorry I didn’t see this until now, the site has forwarded some comments as emails so I thought it would send all of them.

great tutorial, the tutorial’s sequence is better than before. :+1:

I’m from China, My listening is not good, but it worth for me to listen more times. lol

happy new year

thanks! do you know about the subtitles? do they help?

it’s the middle button:

if it doesn’t work in Safari, try using Chrome

happy new year!

Thank you very much , audrey . I even didn’t know the subtitles before.

the subtitle is really great.

Hi Audrey,

I am a bit confused. Sorry for a long chain of questions:
On a Serial Queue what happens if we dispatch tasks asynchronously ?
Does that mean the tasks are performed on another thread and the serial queue continues to dispatch other tasks?If so isn’t this how a concurrent queue will function? Whats the difference between a Serial queue dispatching a task asynchronously and a concurrent queue dispatching asynchronously ?

hi ruban: I found a better way to explain sync vs async and serial vs concurrent, and mention it later in this course.

You always dispatch a task from a source queue to a destination queue.

  • sync vs async is about the source queue: sync blocks the source queue; async lets the source queue continue with the next instruction.
  • serial vs concurrent is about the destination queue: tasks run one at a time on a serial queue, but more than one task can run simultaneously on a concurrent queue.

If you dispatch async from a serial queue (like the main queue) then the source queue can continue with the next instruction — but only one instruction at a time, because it’s a serial queue.

A concurrent queue could be running several tasks at once, and each task could be dispatching to other queues.

1 Like

Hi Audrey,

Very good course. I am learning a lot.

I’m not using playgrounds, but following along with the examples by re-doing all the demos as simple iOS apps.

For the last part of this chapter entitled “Similar Independent Synchronous Tasks” you dispatch 12 tasks onto a global, concurrent Queue. You mention that the tasks are dispatched in sequential order which make sense. However, when I put my print statement within the dispatch I noticed something strange – there is a random element to the printing.

Here is my code:

    let globalQueue = .userInitiated)
    for i in 1...15 {
        globalQueue.async {
            print("Starting Task Number \(i)")

And the output:
Starting Task Number 5
Starting Task Number 8
Starting Task Number 6
Starting Task Number 4
Starting Task Number 3
Starting Task Number 2
Starting Task Number 1
Starting Task Number 7
Starting Task Number 9
Task Number 5 slept for 0 seconds.
Task Number 3 slept for 0 seconds.
Starting Task Number 10
… and so on …

Now, I infer this to be that the code was dispatched in order – but there are no guarantees which dispatched code is going to get some CPU execution time. So even though Task 1 was queue first – it waited around for execution and Task 5, dispatched later than Task 1, was lucky enough to get placed on a thread that just happened to get executed quicker.

Is that a proper interpretation of these results?

If I move the print statement outside I get the same functionally as the playground (as would be expected). However, if I move the print statement in the playground to inside the dispatch statement, I would still see the print statements in the proper order. Is that just because the playground is only using one thread for the globalQueue while the iOS App/Simulator is opened to all threads?

Hi, it might take me a few more days to answer your question. I’m in hospital, with a horribly slow wifi, and no computer. I’ll try to answer by midweek.

what are these escaping and non escaping closoure . can u explain a k=little bit about them . thx you

It’s a Swift 3 thing. Greg Heo wrote a post about it, and Apple’s documentation has a section Escaping Closures — scroll down to the last quarter of this page.

Asynchronous functions often have a completion handler argument, which runs after the function returns, i.e., the closure “escapes” the function. From Swift 3, you have to label it @escaping or the compiler flags an error.

Videos 3 and 4 don’t play in safari or chrome.

hi! this has also been reported for parts 7 and 8. @chrisrazeware replied on that thread:

I have our internal Engineering team looking into this for you. I’ll let you know as soon as we find a solution for you!

Hi @bun-chan,

Apologies for this problem. It should now be resolved and you should be able to view videos again.

Let me know if your are still having issues.


Hi Audrey, thank for tutorial


  • serial vs concurrent is about the destination queue: tasks run one at a time on a serial queue, but more than one task can run simultaneously on a concurrent queue.

  • A concurrent queue could be running several tasks at once, and each task could be dispatching to other queues."

let userQueue = .userInitiated)
let defaultQueue =

// DONE: Create asyncAdd function
func asyncAdd(_ input: (Int, Int), runQueue: DispatchQueue, completionQueue: DispatchQueue, completion: @escaping (Int, Error?) → ()) {
runQueue.async {
var error: Error?
error = .none
let result = slowAdd(input)
completionQueue.async {
completion(result, error)

// DONE: Call asyncAdd; use defaultQueue for completion
asyncAdd((1,2), runQueue: userQueue, completionQueue: defaultQueue) { result, _ in
print(“\nasyncAdd result = (result)”)

Because more than one task can run simultaneously on a concurrent queue. Why do not we use only one concurrent ?

==> asyncAdd((1,2), runQueue: userQueue, completionQueue: userQueue) { result, _ in
print(“\nasyncAdd result = (result)”)

Do you use two different Concurrent queue because Qos? or have other reasons?
thank you again :slight_smile:

This tutorial is definitely awesome!!!

1 Like

Hi Audrey,

I have 2 questions:

  • First one is the same as Sedawk’s question since Jul '17.

  • Second one is related to the challenge of part 3.
    Why the following function:
    does not make use of the completion queue? like dispatching the completionHandler to it. While in the for-loop of images you’re passing the default global queue, even though you’re appending the image on the runQueue, like so:

    for image in images {
      asyncTiltShift(image, runQueue: workerQueue, completionQueue: { image in

@audrey Can you please help with this when you get a chance? Thank you - much appreciated! :]

sorry! I never did get back to you on this :astonished:
thanks @mennabah for reminding me!

you’re right about the tasks getting cpu time in random order, not the order they’re dispatched

and also, the playground does behave differently, but I can’t find any way to check; I did notice that it doesn’t really use the main queue, so in the URLSession course, I use the default queue.