Here we are, ready to tackle completion closures.
We've seen many in other people's code, but
now we're finally ready to write our own.
Writing a method that takes a closure as a parameter has a wacky syntax, but
don't worry, we have a great tool called www.goshdarnclosuresyntax.com.
Let's take a look at it.
This is www.goshdarnclosuresyntax.com, and
it's a list of all the different ways that you can declare a closure in Swift.
So you have as a variable, optional variable, type alias, etc.
The one we're actually interested in is argument to a function call.
So we want a function that takes a closure as one of its parameters or
arguments.
There is another version of this site with a far more memorable name, but
our lawyers told me I couldn't use it.
Finding it is left as an exercise to the interested student.
Anyway, let's get back to Xcode.
Now we're going to write a method called withBigImage
that takes a completitionHandler as a parameter.
What will this method do?
It's going to download the image in the background and
once it's finished, once it's done, it will run the completion handler.
The completion function closure, or handler, however you want to call it,
is what it should do with this image once it has it.
So let's get started.
This is the signature for the withBigImage method.
It takes one parameter and this parameter has two different names,
an internal name called handler, and an external one called completion handler.
But the important stuff is here.
It returns nothing, but it takes one parameter, a UI image.
That's because since it's going to do something with a UI image,
it must have access to that UI image that this method is going to provide.
So the completion handler must
take as a parameter the image that has been downloaded.
Next, we will have to fill in the code, and
it's going to be very similar to what we wrote previously.
We are going to need to create a download queue, and then we're going to
add to that queue a closure that downloads the queue in the background.
And then we will finally, once we're done, we're going to run this handler.
And you can start thinking right now in which queue do you think
that the completion handler should run?
Let me write the code and we'll get there.
Okay, this first step is to call the spare chasing.
And we need also a queue,
but this time we're not going to create a queue from scratch,
we're going to reuse one of those four queues that the system allocates for
each application on iOS, and we need to choose also the priority of this queue.
It depends, if it's something very urgent, something that has been
initiated by the user, then it should get a decent priority.
If it's something that can wait, then you could give it a lower priority.
But first we need to get this queue.
The first parameter for
dispatch_get_global_queue is something called the identifier
that's actually a constant that gives you the priority of the queue.
This QOS by the way means "quality of service." Class_Background,
that means pretty bad quality of service.
It's going to have low priority.
And user_interactive is the top priority and
user_initiated is the normal hold priority, I'll set that.
And the flags, those are not being used right now.
That has been set up for the future, so right now you should pass zero.
And we have the closure.
So within this code, that's where we have to get the URL,
download the NS data, turn it into a UI image,
and then, once we have the image, run the completion handler.
Let's write, this should be familiar by now, but let's write it.
So here's the code that we've been writing over and over and over,
it's encapsulated in a nice if-let statement so
nothing evil will happen if we get a nil.
The only thing that is left is to run the completion closure, and
that's just like calling any closure or function.
So it would be handler, image, and the image that we downloaded.
Now look carefully at this code and
think if this is wise, if this is a good idea.
Everything that is running
inside this closure is running in the background queue.
However, completion handlers or completion closures
are usually used to update the UI, so
they will very likely use an axis UIKit.
So this is definitely not a good idea, okay?
It might crash if whatever code is in that handler, we don't know
exactly what's in there, because we get it as a parameter, as a black box,
if that accesses UIKit, this code will crash definitely.
So it's a good practice to make sure that always
all your completion handlers run in the main queue.
This is not a rule, but It's a very good practice.
Make sure that always, all your completion handlers,
those that you write, run in the main queue.
When you get a library from a third party, and
it has completion handlers, you should check the documentation to
make sure where those completion handlers are run.
The usual is the main queue.
So we need to wrap this code here, in a dispatch_async,
dispatch_get_main_queue, and then call the completionHandler.
This should be trivial by now.
Let's finish it.
Here it is, we call dispatch_async, as usual, then dispatch_get_main_queue,
because we want to run the next closure, within the main queue.
And inside this closure we will run the completion handler, so
this way we're also hopping from one queue to the other.
We hop to a background queue here.
Then when we have the image, we go to the main queue and
run the completion handler.
And either if this completion handler uses UIKit or not,
it's okay, the code will not crash.
And now we're finally done.
No, hang on there, this is a very good moment for you to go and take a look
at the black box code that hides the reality of GCD from most students.
It should be obvious to you right now.
Just by staring at the code,
you should be able to see the lady in the red dress.
And there's extra nerdy points for you if you know what I'm talking about.