In this lesson, we're going to update our user interface with the live weather data from OpenWeather 1
Map. 2
And in the process, we're going to learn about the DispatchQueue. 3
Now, that we are able to get hold of the live weather data, inside our didUpdateWeather method in 4
our WeatherViewController which, of course, is able to command the conditionImageView and the temperatureLabel, 5
let's go ahead and use some of this data to set our user interface. 6
The first thing I want to set is the temperatureLabel. 7
And I'm going set its text property to equal the weather.temperatureString. 8
So remember, this was the temperature that's rounded down to one decimal places and turned into a string, 9
perfect for putting inside a label. 10
So let's add that here. 11
And right now if we go ahead and run our app, it will actually crash. 12
So let's again search for the weather in Paris. 13
And as you can see, we've got some messages down here. 14
We've got our red line right here. 15
And in our debug console, we're seeing the error: "Terminating with unquote exception." 16
So what's happening here? 17
Well, notice how if we go back into our WeatherViewController, we've got this error that's occurred 18
at runtime, so while our app was running. 19
And it tells us that UILabel.text must be used from the main thread only. 20
Well, what does that mean? 21
Well, let's click on the question mark. 22
That question mark takes us to the documentation and tells us what the problem is. 23
Now, the problem is that we're trying to update our user interface from inside a Completion Handler. 24
What does that mean? 25
Well, the reason why the completion handler is a completion handler is because it tends to be carrying 26
out long running tasks such as networking which is exactly what we were doing. 27
And these long running tasks are executed in the background. 28
This means that the execution no matter how long it takes to network with OpenWeather Map to ask it 29
for data, and then get the data back, and then travel all the way across the world. 30
This execution happens in the background, so that we don't block the UI because, otherwise, if we said, 31
"Well, let's wait for this process to finish before we let the user use any of the buttons or interact 32
with the app," then the app is going to look like it's completely frozen. 33
So in this case, if we are trying to update a user interface elements, such as a label from within a completion 34
handler like this, 35
so our closure will then we have to call the main thread to update our user interface in the background. 36
This is exactly the problem that we're facing in our weather manager. 37
In order to get our weather data we're having to perform this session task and we're waiting for the 38
task to complete. 39
And this is our completion handler. 40
Once it does complete, however, we call didUpdateWeather and send over that weather data. 41
So that data that we're getting over here in our WeatherViewController is completely dependent upon 42
the completion of that networking session. 43
So, now if we're saying update the temperatureLabel.text with the data from that weather object, 44
which might take a few seconds to a few minutes to get back, well, before we get it back, then there's 45
nothing for our temperatureLabel to set, right? 46
So then, our UI would be completely frozen and our user will think that our app has crashed, even though 47
it is actually because they have really bad network conditions. 48
So instead, what Apple tells us to do is we have to wrap our UI update code within a block called 49
DispatchQueue.main.async. 50
So let's do that. 51
Let's go ahead and paste that and put our code that updates the UI inside a Dispatch.main.async 52
closure. 53
And, of course, because it's a closure, we have to add the word "self" in front again. 54
And now if you run the app, you'll see that our temperature from Paris is able to be updated into our 55
temperatureLabel without any issues. 56
Now that we've solved that problem, we can go ahead and update the other thing which is our 57
conditionImageView. And our conditionImageView is going to have its image properties set and we're going to 58
set it to a user interface image that comes from UIKit. And the way that we're going to initialize 59
it is through using something called a system name. 60
So this expects a string that matches a system symbol image. 61
So those are the names of our SF Symbols. 62
And remember that in our WeatherModel, we've already got this condition name which takes the conditionId 63
that we get back from OpenWeather Map and converted into a String depending on which weather condition 64
code it was. 65
And then we return a name of the SF Symbol that we're going to use. 66
So we can simply tap into this conditionName in our WeatherViewController in order to create our 67
user interface image to put into the conditionImageView. 68
And again, let's add "self" in front just to be clear. 69
And when we run our app, 70
we can now tap into the live weather data in any city we're interested in. 71
So currently in London, it's kind of a thunderstormy 72
and it's about 18.5 degrees. 73
What about somewhere nice? 74
What about Bali? 75
What's it like over there? 76
Also thunderstorms, apparently, 77
but it's much warmer. 78
So have a play with that. And you can try and add the code to update the city name as well. 79
I'll be adding this a little bit later when we're requesting the device location. And in the next lesson, 80
we're going to learn about Swift extensions in a Swift Deep Dive in order to be able to tidy up our 81
WeatherViewController and refactor it. 82
So if you want to learn more about that, I'll see you on the next lesson.