Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Start a free Basic trial
to watch this video
Now that we know the importance of concurrency and how our code might cause problems, let's sort things out. In this video, we're going to use the URLSession class to write concurrent networking code that should steer us clear of any problems.
-
0:00
To stop blocking the main threat and write concurrent networking code,
-
0:04
we're going to use the URLSession set of classes.
-
0:07
Back in our view controller,
-
0:09
let's get rid of the two lines of code using the data type to retrieve it, and
-
0:14
print out the contents of the URL request using JSONSerialization.
-
0:18
So all of that, gone.
-
0:20
Instead of using the data type to fetch our JSON data synchronously,
-
0:24
which is a big no-no, we're going to use the URLSession collection and
-
0:28
its set of related classes to conduct an asynchronous request.
-
0:33
A URLSession object provides us with API
-
0:36
to download content over the web using HTTP.
-
0:40
Either using delegate methods or closures, with a URLSession API you create
-
0:44
a session object which carries out all of your networking tasks.
-
0:49
The first thing we need to do to create a session object
-
0:52
is to configure a URLSession configuration object.
-
0:56
A configuration object defines rules to use when uploading and
-
1:00
downloading data using a session.
-
1:02
So always remember, when uploading or downloading data,
-
1:05
creating a configuration object is always the first step you must take.
-
1:10
The session object lets you configure a bunch of different things,
-
1:13
like authentication and cashing policies.
-
1:16
But we're not going to dive in to the nitty-gritty for this course.
-
1:19
We just want a default configuration so we can get started.
-
1:23
And luckily, there's a class method that creates an object with all the options
-
1:27
set to default.
-
1:28
So we're gonna say let configuration = URLSessionConfiguration.default.
-
1:35
Now that we have a configuration for our networking needs,
-
1:39
we can create a session object to manage the network session.
-
1:42
So we'll say let session = URLSession,
-
1:48
and it takes the configuration and we can pass that through.
-
1:51
Now, another easy way, since this is a static property on the type,
-
1:55
we actually can just get rid of this.
-
1:58
And here, since it knows the type that it wants to accept,
-
2:02
we can say .default, and the compiler knows what we mean.
-
2:06
Using the session object, we can create a task to carry out.
-
2:10
The base class for this purpose is URLSession task.
-
2:14
But there are two subclasses that are more suited for our purpose,
-
2:18
URLSessionDataTask and URLSessionDownloadTask.
-
2:22
Now, which of the two do we use?
-
2:24
URLSessionDownloadTask retrieves the contents of the URL and
-
2:28
saves it to a temporary file on disc.
-
2:31
in contrast, the URLSessionDataTask returns data directly to the app,
-
2:36
in memory, as a data object.
-
2:38
We don't need to save our data to the disc, so we're going to use the dataTask.
-
2:43
A task is created as a method on this session object we just created.
-
2:48
Okay, so we're going to use the dataTask method to fetch our data.
-
2:51
There are two versions of this method.
-
2:53
So let me just start typing it out, so we'll say session.datatask.
-
2:59
And here you can see two versions, we'll focus on these ones,
-
3:02
one that takes a URL and one that takes a request object.
-
3:07
If we use the URL version of the method, a request is created for
-
3:11
us with the configuration options on that request set to some sensible defaults.
-
3:17
So the second version of this method takes a request that is an instance of
-
3:21
URLRequest and performs a GETRequest to fetch data from the web.
-
3:26
The method also has a completion handler that is called when the task is completed.
-
3:31
We'll get into what a completion handler is in just a second.
-
3:34
So we're gonna use this one, that takes a request.
-
3:37
What's a request again?
-
3:39
Well, when interacting with an HTTP server, a client sends an HTTP
-
3:44
request to the server that contains information about the data you want.
-
3:49
Request object encapsulates information about the type of request message,
-
3:55
which in our case is a get method, a URI, and e set of header fields.
-
3:59
We are going to be creating a very basic request object using the URL request
-
4:04
class.
-
4:05
To learn more about HTTP requests in general,
-
4:09
check out the course linked in th Teacher's Notes.
-
4:11
The URL request class has an initializer method
-
4:15
that we can use to construct a request object with a URL.
-
4:20
The forecastUrl we created earlier is an optional URL.
-
4:23
So first, we'll go up here and we'll use a guard, G-U-A-R-D,
-
4:28
guard statement to unwrap this.
-
4:31
Else return, so if we don't have a URL object, we'll just exit viewDidLoad.
-
4:37
Okay, now that we have a URL, up here, let's create a request.
-
4:41
So we'll say let request = URLREQUEST.
-
4:47
This takes a URL, and we can pass in the forecastUrl.
-
4:51
Now, when we do this, we initialize a request with a URL,
-
4:54
it creates an HTTP request that defaults to a get request.
-
4:59
Now that we have a request, we can use this to create a dataTask.
-
5:03
So right below, we'll say session.dataTask, and as you can see,
-
5:08
there are four versions of the method.
-
5:09
First, we're gonna choose the one that takes a URL request.
-
5:12
And then second, we're going to choose the one that takes a URL request and
-
5:16
a completion handler.
-
5:17
Here we'll pass the request through.
-
5:20
The second parameter in the dataTask is a completion handler.
-
5:25
A completion handler, this one that you see here, has a type or
-
5:28
a signature that seems like a function.
-
5:31
And that's exactly what this is, it is a closure.
-
5:34
The function takes three arguments, an instance of data, optional data, and
-
5:39
instance of optional URL response, and an optional error.
-
5:44
Return type is void.
-
5:46
A completion handler is a closure that we define
-
5:49
containing the logic of what we want to do after this request has completed.
-
5:54
When we write asynchronous code, because it happens in the background,
-
5:59
we cannot control when our code is executed.
-
6:03
For this reason, this method has no return type.
-
6:07
Rather it doesn't give us a data object back
-
6:10
like the method we used earlier because it's not going to finish right now.
-
6:15
So over here you see, when you call this method, it executes immediately.
-
6:19
It's a synchronous method, it has a return value that we can assign to a constant.
-
6:24
These methods don't work that way.
-
6:26
Because it's asynchronous and it's shuttled off into the background for
-
6:30
the system to handle with, we have no idea when it will be finished.
-
6:34
So what we do is we define a function containing some logic.
-
6:38
We define a closure containing some logic, and this closure is packaged up.
-
6:44
And the system executes this closure whenever the request is done.
-
6:49
Now we use a closure because a closure can capture references to
-
6:52
all the objects we need and execute the logic at a later time.
-
6:57
The closures used in this way are called callbacks.
-
7:00
The dataTask is executed, and
-
7:03
then when it is done, it calls back to our closure and executes the body.
-
7:07
Another way we can implement the same kind of behavior is by using
-
7:10
the delegate pattern.
-
7:12
An object can complete the task, and
-
7:14
when it's done, it can inform or call back to the delegate.
-
7:18
The delegate then performs further actions with whatever data is returned.
-
7:23
When we learned about closures, we defined a simple function.
-
7:26
Let me throw that up on the screen again.
-
7:28
We defined a function that accepted, as an argument, a function named operation.
-
7:33
And here we can write a trailing closure.
-
7:36
In the body of the function, we call the function or
-
7:38
closure expression and provided argument.
-
7:41
The method we're using, dataTask,
-
7:43
works in the same way, except we haven't defined it.
-
7:47
So we don't know what the body of this method looks like.
-
7:50
This function accepts another function, or a closure expression.
-
7:54
And our job is to provide that closure.
-
7:58
Much like our apply operation had a single argument, this closure accepts three
-
8:03
arguments, an instance of data, containing the data returned by the network request,
-
8:08
an instance of URL response, which defines the response returned by the remote
-
8:13
server, and any errors that may have occurred.
-
8:17
All of these objects are optional because if we have valid data,
-
8:20
then we won't have an error and vice versa.
-
8:23
Now, inside this closure, we define what we want to do with these instances.
-
8:28
When the dataTask is complete,
-
8:31
just like we called operation in our closure and passed in an argument,
-
8:36
the dataTask method will call our closure and provide the data, response,
-
8:41
and error objects it obtains by making the network call as arguments to our closure.
-
8:48
So in this way, we define a callback.
-
8:50
Now back to our code, the completion handler takes a closure expression with
-
8:54
three parameters, optional data, optional response, and optional error.
-
8:59
The bulk of our work is going to happen inside this closure.
-
9:02
So let's take a detailed look at these arguments.
-
9:05
And the first argument here contains
-
9:07
a data object with the results of our get request.
-
9:11
Just like when we used the initializer, contentsOf, directly on the data type.
-
9:16
Now the second argument here is a response object.
-
9:19
URLResponse contains a bunch of metadata about how the request was carried out so
-
9:25
we can check to see if the request succeeded.
-
9:27
And then if it did, we can access the data.
-
9:30
Finally, if we have any errors, they're stored in the error object.
-
9:34
Since this is the last parameter in the function, we can write this
-
9:39
out as a trail enclosure, so we have data, response, error in.
-
9:44
Now, inside the crucial body for now, I'm simply going to print out the data
-
9:49
object to the console to see if this works.
-
9:53
So what I'm doing here is I'm defining a closure and
-
9:56
giving that to this dataTask method.
-
9:58
The body of the closer just simply says, print the data.
-
10:01
Whenever this dataTask is complete, it's going to unpack this disclosure,
-
10:06
look at the body, and do whatever the body says, which is print the data.
-
10:10
It may seem like it's happening immediately,
-
10:13
like it's a synchronous method, but again, we're fetching very little data, and
-
10:17
this is happening very quickly.
-
10:19
So it just feels like it, but it is indeed happening in the background.
-
10:23
Now, this method dataTask that we're calling on the session object
-
10:27
creates that task for us.
-
10:30
Along with the closure we define, packs it all together, and
-
10:33
returns that task itself.
-
10:35
So we can assign this back to a constant.
-
10:40
Now, it's very important to keep in mind that what is being returned here is not
-
10:44
a return type from executing the network request.
-
10:48
All we're doing here is creating a task,
-
10:50
we're not calling out to the network at all right now.
-
10:54
And you can verify this by running the app and waiting, and
-
10:59
nothing is going to work because all we're doing here is creating the task.
-
11:02
There won't be anything printed out.
-
11:04
To run the task, we actually have to call dataTask.resume.
-
11:09
You may be confused, I never called start or stop, so how can I call resume?
-
11:14
Well, that's just the way Apple wrote this API.
-
11:17
The task is added to the session and made active when we call resume.
-
11:21
When it's done, the task automatically stops.
-
11:24
So now, if we go ahead and run it, may take a second, but just like before,
-
11:29
you should see the byte information pop up in the console.
-
11:33
You'll see that it's optional because remember,
-
11:35
this is an optional instance of data.
-
11:37
How is what we did different, though?
-
11:39
The difference is mainly how URLSession conducts a network request.
-
11:43
The APIs are asynchronous,
-
11:46
which again means that it is not carried out on the main thread.
-
11:50
Our dataTask is carried out on a background thread.
-
11:53
And when the task is complete, we use the closure as a callback mechanism.
-
11:58
Now, you can see this in action by adding a couple log statements.
-
12:01
So right before the dataTask is carried out, we'll say print ("First").
-
12:09
And then in order, we'll say print ("Second") over here, right,
-
12:13
because we're writing this line of code after.
-
12:16
And then right below, we'll say print ("Third").
-
12:22
If I run this again.
-
12:26
You should see something unusual.
-
12:29
So instead of printing in the order it was written, First, Second,
-
12:32
Third, we have First, Third, and Second.
-
12:35
And that's because obviously we hit the print statement First first, and
-
12:40
we print it out, but this is just packaged up and given to the background.
-
12:45
Immediately then we call Third, and
-
12:48
then once the task actually starts in the background and finishes, it calls Second.
-
12:52
Now, again, you might say, well, Third is called before you start the task, so
-
12:56
that's why it's happening.
-
12:58
But if you put it down below here and
-
13:00
run this again, you'll see that we get the same results.
-
13:04
So First, Third, and then Second.
-
13:06
And that's because anything that's inside this closure is executed in
-
13:09
the background, whereas everything else is on the main thread and
-
13:13
happens immediately.
-
13:14
All right, let me get rid of this.
-
13:16
So this is a much better implementation than our previous one
-
13:20
where we blocked the main thread.
-
13:22
But if you remember, I mentioned that we were doing more than one thing wrong.
-
13:26
Well, we just talked about the first issue, writing synchronous code,
-
13:29
and the second one is that our code is tightly coupled.
-
13:33
Everything is in the view controller.
-
13:35
Now, this is bad because we can't reuse any of this code.
-
13:39
Let's fix that.
-
13:40
There's also a third issue,
-
13:41
but we'll wait a second, we'll talk about that in a few videos.
You need to sign up for Treehouse in order to download course files.
Sign up