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