3.2 Request and Response
URLResponse are the essential objects that are being handed around during network requests. In this lesson, I’m going to show you how you can even further customise your requests and what you can get out of a response.
1.Introduction3 lessons, 12:12
2.Animation3 lessons, 21:28
3.Networking3 lessons, 23:55
4.Custom Controls3 lessons, 32:22
5.Conclusion1 lesson, 01:44
3.2 Request and Response
Hi, and welcome back to Go Further with Swift, where we build a weather app for iOS. In this lesson, I will talk about the request object and responses in more detail. We are back in the API class, and first let's do some cleanup of the code we created last lesson. We don't need to create the task in the initializer anymore. Instead, I'm going to create a public function called getWeatherData that receives a latitude and longitude as parameters as well as a completion block. This completion block receives the data as an optional any object and maybe an error, too. Let's create the data task again, but this time we're doing it with a request object. The block is going to be the same. What does this request look like? Well, it is a URLRequest object, and can be either created with just the URL or the additionally a cache policy and a timeout interval. I'm using the shorter one, since these two values don't really affect us. You can set request-specific properties on the request, like additional HTTP header fields that are not present in the session. I'm going to add application/json as a value for the Content-Type header for example. In earlier versions of the framework, you had to set mutable URLRequest to make changes to this request, which is now deprecated. Mutability is controlled by using the let or var keyword to define the variable. You can also set specific settings on the request that are defined in the session as well. But you can't be more permissive than in the session. For instance, you can't allow cellular access if the session forbids it. The other way around works, however. Like in the last lesson, let's add a guard clause to ensure we have some data to work with. Otherwise, let's print the error's description and return. I'm also going to check for an HTTPURLResponse. Since the task doesn't necessarily have to be an HTTP request, it supplies a generic URLResponse to the block. If there isn't a HTTP response, I'm going to call the completion block with nil and the error, since we can't handle the response data. Let's have a look at some of the specific response properties. One is the status code, which is the HTTP code returned by the server. We can also get all header fields that were sent. Of course, we're most interested in the JSON data. I'm using JSON serialization to parse the string into an object. Since this function can throw an error, I'm using the soft try keyword that will set the JSON variable to nil if the JSON object call fails. The print statement is not quite happy with an optional, so it warns me about it. There is a neat syntax for using a default value in Swift when a nil check fails. The double question mark operator checks the first option for nil, and if it has a value, it is using that, otherwise it uses the second option. Now that we have created the function to make a request, let's use it. In the project setup lesson, I've added the didChangeAuthorization delegate callback, which we now want to use. We tell the manager to start updating the location. We'll wrap an if statement around it to verify that we have permission to do so. In another callback method that update the locations, we're going to get the first location of the locations array and make the call to the API. Since we have a location now, we can use the coordinate of it to fill our parameters. Of course we need a completion block that handles everything after we receive the response. First let me check if we have data. Then I can set some variables that parse the dictionary. Since JSON serialization returns an Any object, we have to cast everything to the correct type. I know about the different keys from the documentation. The main data contains the temperature information. The weather key contains an array of multiple conditions that hold the weather information. Let's start updating the user interface. First the city name, which is in json name, then the temperature as an integer. I need to forcefully unwrap the items, since they are optionals. The condition label gets the first condition's description and will be displayed as a capitalized string. For the condition icon, I'm using the ID to switch between different conditions. The documentation shows what the different IDs mean. And there are quite a lot of them. But luckily, they are grouped into categories like HTTP status codes. So everything in the 200 region is going to be a storm. So, I'm setting the conditionImageView's image to the storm image. 300s contain drizzling weather. The 500s contain actual rain. And in the 600s there is snow. Everything else will get the default image of clear. Although a sun might not be appropriate for a hurricane. Let's have a look at all this in the app. After building and running, we can see that the request was successful in the console, but the interface doesn't update. There is a simple reason behind that. Our network requests don't run on the main thread. But the user interface must only be updated on the main thread. Normally, the convention that you always call a completion block on the main queue is the way to go. So in our getWeatherData function, we can add a call to OperationQueue.main.addOperation, and wrap it around the completion blocks. This ensures that these blocks will run on the main thread. Now the updates work. Innere Stadt is Vienna's first district. And we have misty weather at 0 degrees. To improve the display of the temperature, I'm going to paste in a degree sign to the weather label. Now let's change the location. Sometimes when making very big hops between locations, the update fails and we have a very restrictive callback. So let's remove the call to fatal error. In Cupertino, there's also misty weather, and it currently has 8 degrees Celsius. When you look at the console, we are doing a lot of repetitive calls to the API, because location manager sends us frequent updates even if the location didn't change. I'm going to limit that by checking if we actually moved between updates. For that, I'm going to calculate the distance between the previous location and the current one. If we don't have a previous location or the distance is greater than zero, we will make the request. I also need to add the previous location variable to the class. And set it to the current location at the end. After building and running again. You can see there is only one request being made. And you can also see all the HTTP headers we printed before that are sent with the response. In the next lesson, we will have a look at operation queues and how to use them to make concurrent requests. See you there.