In this lesson, we're going to be learning how to parse the response that we're getting back from 1
OpenWeather Map's servers. 2
So now that you've seen how closures work, let's convert our completion handler here to an actual Swift 3
closure. 4
Instead of creating session.dataTask with URL and passing in the completionHandler as a 5
actual method, I'm going to go ahead and initialize this dataTask again, because I'm going to borrow 6
Xcode's code completion to help me. 7
So I'm going to choose the one where I give it a URL and I end up with a completionHandler that 8
triggers 9
once it's done with the task. And the URL is, again, going to be just the URL that we got from 10
our optional binding. 11
But here in the completionHandler, where you see this gray area, which is the code placeholder that 12
is telling us what it's expecting. 13
If you go ahead and select it, so turn it blue, and then hit enter, 14
it should format it into a trailing closure automatically for you. 15
So now that we see this familiar closure format, we can go ahead and add in the names of the inputs. 16
So that's going to be called data, that's going to be called response, 17
and that's going to be called error. 18
And now inside this code block, we're going to move in everything that used to live in our handle method, 19
and it's now going to go inside our closure. And we can delete the separate method. And then I'm going 20
to select everything and hit control I, or whatever your shortcut might be if you go to Editor, Structure 21
and Re-indent. 22
So this is what I see. 23
You might have something different. 24
And now I've reformatted my code to look a lot more Swifty with my anonymous function here. 25
And I've got my trailing closure which is going to carry out this task once it completes with the 26
dataTask. 27
So now that we're done with getting hold of the data, we don't really want it as a dataString because 28
this is impossible to read even if I concentrate and really squint my eyes. 29
Instead, we're going to pass this piece of data and turn it into an actual Swift object with properties 30
and methods. 31
So to pass this data which is currently in a JSON format and I know it's a JSON response because when 32
I look at the documentation, you can see that the example of an API response in the JSON format looks 33
exactly like what we've got. 34
And JSON formatted data that's being transported across the internet, is probably one of the most popular 35
and one of the most widely used formats. And other formats like XML is fast going out of fashion. 36
But what exactly is a JSON? 37
What does it actually stand for? 38
Well, a JSON stands for JavaScript Object Notation. 39
This actually comes from the JavaScript world, the web world, where there's a lot of data being transported 40
across the internet, and they need to make it as efficient, as lightweight, and as small as possible. 41
I want you to imagine that you start out with a wardrobe and this wardrobe happens to be an object that's 42
created in JavaScript. 43
So this is where a JavaScript object would look like. And that created kind of almost like our dictionaries. 44
But in this case, the object is called wardrobe. 45
It has a property called doors which is set to equal to a property called drawers and a property called 46
color. When this is being transported across the internet in order to save space, 47
we're going to get rid of all this formatting which doesn't really matter while it's in transit anyways. 48
And we're going to turn it into a flat pack wardrobe like the ones you get from IKEA. The flat pack wardrobe 49
still retains all of the most important parts of the data, namely the syntax, the properties separated 50
by commas, and each object encased in a set of curly braces. But it gets turned into a string, rather than 51
an actual object. But once it arrives on the other side, once you've gotten it delivered from IKEA and 52
you've managed to find all the screws which are impossible to find, and you've built your wardrobe back 53
up again, then you still have a living, breathing, large wardrobe. But during transit, it was turned into 54
a much smaller format so that it can be transported easily. 55
Now, in our case, we don't actually want to unpack the JSON into a JavaScript object. 56
What we want is a Swift object to come out of it. 57
So we would initialize our Swift object with either a struct or a class constructor with properties 58
like doors, drawers, and colour. 59
So let's see how we can do this. 60
So I'm gonna go ahead and delete my comments, just so that it's easier for you to see what's going on. 61
Feel free to keep your comments or any additional things that you have in your code if you wish. 62
But I just want to tidy up the code a little bit so you can see more at a glance. 63
So down here below performRequest, I'm going to create another method which is going to be called 64
parseJSON. 65
And this is going to take a single input which is going to be the weatherData and this is, of course, 66
going to be the format of data which is the format that we get back from our dataTask. 67
So then we should be able to call our parseJSON method here and pass over this unwrapped safeData 68
which meets the requirements of being a data object. 69
But notice how we're getting a error here telling us that call to method 70
'parseJSON' inside a closure requires an explicit 'self.' 71
Normally, when we call methods such as when we called performRequest, we didn't actually need to say 72
who was the object that owned this method performRequest to call this method. And in fact, the full way 73
of writing this code would have been 'self.' 74
So call the method performRequest from this current structure which is here. Now, the reason why we didn't 75
have to do it is because Swift is actually doing it for us under the hood, and this keeps our code nice 76
and tidy, and we already can see that performRequest is right there inside our WeatherManager, 77
so it is superfluous to add the 'self' 78
in this case. 79
But when we are inside a closure, then it can get confused about where to add the"self" and where to not 80
add the "self," and where the method you're calling exists. Because remember our data task is triggering 81
this method and when it gets triggered, then everything inside gets executed. 82
So inside the closure, the rule is we must add the word "self" if we are calling a method from the current 83
class which we are right here. 84
So that fixes that error. 85
And now we can focus on our parseJSON method. In order to parse our data from a JSON format, 86
we first have to inform our compiler how the data is structured. And we can do that through the use of 87
a structure that we create. 88
So in the Model folder, again, I'm gonna create another file which is a Swift File, and I'm gonna call 89
this the WeatherData because inside this file, I'm going to create the structure that's the WeatherData 90
is going to come back in. 91
Notice how if we have our data formatted with JSON Awesome, you can see it as a tree structure. 92
So we're getting back an object that has 13 properties. And the first one is a property called coordinate 93
which inside is another object that has a longitude and latitude properties. And we also have a property 94
called a weather which inside actually has an array with a single item. 95
So we have to basically break down this structure for the compiler to be able to chew through the data 96
that it's getting in this format, and then we can assign property names, and we can turn it into a Swift 97
object. 98
So let's start with something quite simple, something that's just got a key and value pair. 99
So for example, our name here. The property name is called name and the value is Paris. 100
In this case, we could create our structure WeatherData and it can have a single property code name. 101
And the name 102
if we check is, of course, a string because it's got the quotation marks around it. 103
So we'll sign it, 104
the date type of a string. 105
But this is not quite enough. 106
In addition, we also have to say that our struct conforms to the Decodable protocol and that means 107
that the WeatherData turned into a type that can decode itself from an external representation namely 108
the JSON representation. Now that our WeatherData is Decodable and it's got a property called name, 109
we can go back into our parseJSON method and create a decoder. 110
And we're going to create that from the JSON decoder object, 111
so this is an object that can decode JSON. And we're going to just initialize it. 112
Next, we're going to use our decoder and we're going to decode. 113
Now, this takes two inputs. 114
Firstly, the data that you want to decode and a Decodable type. 115
So a data type that we're using to decode that conforms to the decoder protocol. 116
Well, we've got one of those right here. 117
Our WeatherData is a Decodable type. 118
So notice how in this case, it's expecting a type in here as the input, not an object. 119
So to specify the weatherData type, rather than just the weatherData object, we have to add a ".self" 120
to reference the type. 121
So if I click on this fix button, it turns my WeatherData into a data type that I'm passing in. 122
Now, finally, the data that we want to encode is going to be passed in as the last input and that is going 123
to come from this weatherData right here. 124
So that's what I'm going to put there. 125
So, now we should have decoded our data. But we're still getting errors because it's telling us that this 126
call to decode can throw but is not marked with a 'try' and the error is not handled. 127
What does that mean? 128
Well, if we take a look at this decode method again, you can see that it's marked with this "throws" keyword 129
which means that if something goes wrong, it can throw out an error. 130
And so instead of simply just calling it as we would with other methods, 131
all we have to do is we have to market with a "try" keyword. 132
And in addition, we should really wrap it inside a block. 133
That's called a "do" block. 134
So the "do" block has the thing with the "try," and that marks the method which can throw an error. 135
But if it does throw an error, we have a catch block that can catch the error. 136
So we could simply print the error in here. So now our error goes away and Xcode is now happy with the 137
way that we're programming our project. 138
The final thing we have to do is that decode actually has an output, 139
so it's going to create a WeatherData object. 140
Let's go ahead and capture that in a constant. We'll call it decoded data and we'll set it to the output 141
of this method call. 142
And then there's decoded data because it has the data type of our WeatherData which means that it has 143
the property name. 144
Well, then we can actually print out decodedData.name. 145
Now, let's go ahead and run our app. 146
And if I go ahead and type in the name of a city, say, "Belgrade," and hit enter, you can see that the name 147
of the city is being printed, but it's not being printed from my WeatherViewController. 148
There is no print statements in my text field delegate methods. 149
In fact, the only one that's being triggered is this decodedData.name. 150
So some of you might be thinking, well, you could be cheating," that might still be somehow coming from 151
the text field. 152
Show me a piece of data that comes from the API that we didn't enter into the app ourselves. 153
Okay. 154
So I'm always up for a challenge. 155
So if we take a look at our result that we get back, 156
notice how we're getting the temperature of the location back as well. 157
Let's try and get hold of the temperature. 158
Now, a really handy thing that the JSON Awesome plugin can do is when you select one of these properties 159
by clicking on it, you can click on this green button that comes up and copy the path that it would take 160
to reach this value. 161
So, now inside here instead of decodedData.name, I'm going to delete the name and I'm going to paste 162
what I copied over from JSON 163
Awesome. 164
So it's telling me that in order to get to the temperature, I have to take the entire piece of data, 165
go to the main property, and in the main property, go to the property temp in order to get that temperature. 166
And that is what the entire path would look like. 167
But right now, we haven't defined these properties in all WeatherData. 168
So let's go ahead and do that 169
to make these errors go away. 170
Firstly, we've got the top level which comes from our WeatherData and the top level property name is 171
called "main." 172
So that's going to be what we'll add in here. 173
Main. 174
But what data type is it going to have? 175
Well, it's actually a object with five items, and that object has these properties: temp, pressure, humidity, 176
et cetera. 177
That means we actually need another struct which I'm just going to call Main. 178
And this, again, has to be Decodedable. 179
And then, this main is the one that's going to have that property temp. 180
And remember that when you're creating these properties, it's a little bit different from how we would 181
do it 182
normally when it's entirely all code. We can't just name these property names whatever we want. 183
For example, if I call this temperature, then this decoding process is not going to work, 184
so I have to make sure that my property names match the names of the properties that I'm seeing in my 185
JSON data 186
precisely. 187
So this temp that I get back is a number with decimal places. 188
So let's make that a Double data type. And now that we've created this Main struct, we can use it here 189
as the data type of the main property. 190
So let's add that right there. 191
Now, this error goes away, but the error inside the Weather Manager also goes away because our decoded 192
data which is of data type weatherData, now has a property called Main which holds a Main data type, 193
which has a property of temp. 194
So three steps to get to the temperature. But if we go ahead and run our app and I type in, let's see 195
what the weather's like in Paris again, hit Enter. You can see that I'm getting the temperature printed 196
now from our decodedData.main.temp. 197
And in Paris, it's a lot nicer than in London 198
as usual. So now for the final thing that we want, 199
what if we wanted to get a hold of this description right here, 200
how would you go about getting it using our JSON decoder? 201
So this is going to be set as a challenge for you. 202
Take a look at the data in Chrome using the URL and using the JSON Awesome tool to get the path 203
of this particular description. 204
Remember that this weather property holds data inside an array, and that's my hint to you for making 205
this work. 206
Try and get this description printed when we request for the data in our text field for a particular 207
city name, and pause the video and try and give it a go now. 208
All right. 209
So the first thing I'm gonna do is I'm going to copy the path and, again, I'm going to replace 210
my main.temp with whatever I copied over. 211
So in this case, it's going to be from our decoded data going to go straight to the weather property. 212
And then within the weather property where I've got a array with a single item, 213
well, I'm going to get the first item in that array. 214
So, now inside this array's first item, I've got a object that has a property name of description, so then 215
description goes in at the end, 216
and that's how I'm gonna get hold of that description. 217
So, now all we have to do is to map that structure in our WeatherData file. 218
The first thing we have is a property at the top level called weather. 219
So I'm going to create a property called weather that matches that exactly. 220
Next, I'm going to create my weather property which is going to be a WeatherData type. And just like 221
the Main data type, we have to create a new object. 222
But this time, instead of simply just having a weather struct as the value of the weather property, 223
it's actually an array of weather items, 224
right? 225
So the weather property holds an array. 226
How can I represent that? 227
Well, I can go ahead and simply wrap this inside a set of square brackets. 228
Now, I can create my weather structure which has a property called description. And so here let's create 229
our description property which is going to be a string, and make sure that our Weather struct 230
is, again, Decodable for our JSON decoder. 231
Now, if we go ahead and run our code, then the errors go away. And let's check the weather for Beijing. 232
And you can see that in Beijing there is clear skies all around and we're getting that through decoding 233
our JSON data using the JSONDecoder from Swift, and we formatted the data using these Decoable 234
struct, 235
and finally, we're printing it out. If it's still confusing, 236
I recommend just taking a look at your JSON, trying to get hold of different values from this JSON, 237
and having a practice by adding more structs and properties. But in the next lesson, we're going to use 238
our decoded data and format the data so that we can put it into our UI. For all of that and more, 239
I'll see you on the next lesson.