0 1 00:00:00,480 --> 00:00:10,410 In the last few lessons, we've managed to create a Weather Manager object which is able to perform the 1 2 00:00:10,470 --> 00:00:15,140 networking that fetches the live data from OpenWeather Map, 2 3 00:00:15,300 --> 00:00:24,310 and then we're able to pass the weather into an actual Swift object which we can access in a Swifty 3 4 00:00:24,330 --> 00:00:26,810 format like this. 4 5 00:00:26,910 --> 00:00:34,500 In this lesson, I want to focus on getting hold of our weather conditions because we need to be able 5 6 00:00:34,500 --> 00:00:40,470 to update this image view with a different image from the system's 6 7 00:00:40,470 --> 00:00:45,300 SF icons, so that it represents the condition of the weather. 7 8 00:00:46,700 --> 00:00:53,450 Now, if we take a look at the API doss for OpenWeather, you can see that they've got this section on 8 9 00:00:53,570 --> 00:01:03,620 list of condition code. And this section here links to all of the condition codes that they use, and that 9 10 00:01:03,620 --> 00:01:09,110 refers to the ID property in the data that we get back. 10 11 00:01:09,890 --> 00:01:12,790 What do these IDs actually correspond to? 11 12 00:01:12,920 --> 00:01:16,800 Well, they're a way of encoding different weather conditions. 12 13 00:01:16,940 --> 00:01:23,780 So, for example, everything from 200 to 232, encode some sort of thunderstorm, and there's 13 14 00:01:23,840 --> 00:01:27,850 apparently different types of thunderstorms which I didn't really know about, 14 15 00:01:27,920 --> 00:01:32,070 like a ragged thunderstorm. I don't want to be in one of those. 15 16 00:01:32,120 --> 00:01:40,460 Now, we know that we can get hold of the ID which is the ID of the weather condition and we can match 16 17 00:01:40,460 --> 00:01:46,440 it against these values to see what type of image we should show the user. 17 18 00:01:47,270 --> 00:01:53,870 So let's go ahead and do that. Instead of getting hold of the description which we're not actually gonna 18 19 00:01:53,890 --> 00:01:54,800 use, 19 20 00:01:54,920 --> 00:02:02,930 instead, I'm going to change this property to "id" and the "id" is an integer data type. And I can do 20 21 00:02:02,930 --> 00:02:09,920 this because the "id" is actually nested inside at the same level as the description. 21 22 00:02:09,980 --> 00:02:17,480 So changing that over from the description to the "id" or you can if you want to just simply added on 22 23 00:02:17,630 --> 00:02:24,710 as an additional property if you plan on using the description at some later stage. But they both live 23 24 00:02:24,770 --> 00:02:32,510 within this Weather struct which is an array of weather conditions that we're getting back which comes 24 25 00:02:32,510 --> 00:02:34,650 from the entire WeatherData. 25 26 00:02:34,670 --> 00:02:40,910 So following it down, this is the first property which goes into an array, which then has an object with 26 27 00:02:40,940 --> 00:02:42,280 a property code "id." 27 28 00:02:42,350 --> 00:02:47,650 So quite convoluted, but eventually, we should be able to get a hold of this number. 28 29 00:02:47,710 --> 00:02:54,790 Let's go ahead and try to print that in our WeatherManager just to make sure that it's actually working. 29 30 00:02:56,590 --> 00:03:03,490 So let's see what it's like in Bali at the moment. And the weather condition code I'm getting back is 30 31 00:03:03,580 --> 00:03:04,900 803. 31 32 00:03:04,900 --> 00:03:13,940 And if I check against this table 803 is broken clouds, 51 to 84 percent. 32 33 00:03:13,960 --> 00:03:14,950 Interesting. 33 34 00:03:14,950 --> 00:03:21,280 So in this case, I would probably just show a cloud in my app right here. 34 35 00:03:21,370 --> 00:03:29,770 So this is the perfect opportunity to utilize what you've learned about Swift switch statements a few 35 36 00:03:29,770 --> 00:03:30,920 modules back. 36 37 00:03:31,210 --> 00:03:39,590 So I'm going to set this to you as a challenge. Given that we have this ID which I'll call 37 38 00:03:39,640 --> 00:03:41,370 let id = decodedData.weather [0].id, 38 39 00:03:41,380 --> 00:03:50,050 I want you to create a function down here that we'll call 39 40 00:03:50,170 --> 00:03:58,660 getConditionName, and it takes the input in the format of the weatherId which is going to be an integer, 40 41 00:03:59,500 --> 00:04:07,300 and then inside this function, we're going to return a String. And that string will depend on the Weather 41 42 00:04:07,300 --> 00:04:07,940 ID. 42 43 00:04:08,170 --> 00:04:16,420 So we're going to call this getConditionName and pass in the id right here that we get back, 43 44 00:04:16,420 --> 00:04:24,850 and then inside this method was somehow going to use a switch statement to output a string, and that string 44 45 00:04:24,910 --> 00:04:29,520 should match the name of one of these symbols. 45 46 00:04:29,530 --> 00:04:36,550 So, for example, if we were getting clouds, then maybe we would simply return the word "cloud" which would 46 47 00:04:36,550 --> 00:04:39,310 match with this image's name. 47 48 00:04:39,490 --> 00:04:47,320 And if we got, say, rain then maybe we would put cloud.rain to get this symbol. Because, eventually, 48 49 00:04:47,320 --> 00:04:55,130 what we're going to do is we're going to be using that condition name to change this image view here. 49 50 00:04:55,150 --> 00:05:01,990 So if we got cloud.rain as the condition name, that's what we would see. If we got just a cloud, 50 51 00:05:01,990 --> 00:05:10,260 then we would get this image. If we got sun.max, then we would get this image. 51 52 00:05:10,260 --> 00:05:18,210 So feel free to pick and choose whichever icon takes your fancy as long as they more or less match the 52 53 00:05:18,240 --> 00:05:23,320 condition name that we're getting back from Open Weather Maps table. 53 54 00:05:23,550 --> 00:05:30,750 And then when you get this condition name back, I want you to go ahead and print it, so that when you 54 55 00:05:30,810 --> 00:05:38,010 run the app and you put in the name of a location, say in Paris, and you go ahead and get the weather 55 56 00:05:38,010 --> 00:05:45,630 for it, you should be able to get a name for a SF symbol that matches the weather condition of that 56 57 00:05:45,630 --> 00:05:47,310 location. 57 58 00:05:47,340 --> 00:05:52,800 Now, if you already know all about the Swift switch statements and you don't want to complete this challenge, 58 59 00:05:53,130 --> 00:05:58,920 then you can also head over to the README file and you'll find the switch statement already written 59 60 00:05:58,920 --> 00:06:03,350 for you. But I recommend that more practice is always good. 60 61 00:06:03,390 --> 00:06:10,020 So using a Switch statement, try and print out the name of an icon that matches the condition ID from 61 62 00:06:10,050 --> 00:06:11,330 OpenWeather Map. 62 63 00:06:11,370 --> 00:06:18,470 I'm going to leave it up to you to pause the video and complete the challenge if you want to. All right. 63 64 00:06:18,500 --> 00:06:26,660 So here's our Switch statement. And it looks at the weatherID that we get passed over and takes a 64 65 00:06:26,660 --> 00:06:35,540 look at its value, and tries to match it against these cases, and these cases I've put in as ranges. 65 66 00:06:35,570 --> 00:06:42,830 So the range between 200 and 232 in the weather condition codes matches a thunderstorm. 66 67 00:06:43,400 --> 00:06:50,210 So out of all of these pictures of thunderstorms, I think the cloud.bolt looks quite nice. 67 68 00:06:50,390 --> 00:06:58,550 So I've decided to return cloud.bolt, and I've done basically the same thing to match all of these 68 69 00:06:58,640 --> 00:07:00,310 different conditions. 69 70 00:07:00,830 --> 00:07:06,770 And in the default condition where none of these cases match, then I'm just gonna return cloud as the 70 71 00:07:06,770 --> 00:07:09,630 default option. 71 72 00:07:09,660 --> 00:07:16,650 Now, if you are at all confused about Switch statements or the range operator, then be sure to head back 72 73 00:07:16,650 --> 00:07:23,010 to some of those Swift Deep Dives where we talked about those things in more detail. And that's the solution 73 74 00:07:23,010 --> 00:07:31,660 to the challenge. But now you might be noticing that this file is getting a little bit messy and we're 74 75 00:07:31,660 --> 00:07:40,780 getting to the point where we kind of need to group some data in a custom data type. And that is, of course, 75 76 00:07:40,840 --> 00:07:43,480 the role of the data model. 76 77 00:07:43,480 --> 00:07:44,880 So let's do that now. 77 78 00:07:44,920 --> 00:07:49,550 Let's reorganize our code in accordance with the MVC design pattern. 78 79 00:07:49,570 --> 00:07:54,550 This means that we'll take some functionality out of the WeatherManager and put it into a separate 79 80 00:07:54,550 --> 00:07:55,330 file. 80 81 00:07:55,330 --> 00:08:02,980 That way, we'll have shorter and simpler files that are easier to understand. If I want to create a custom 81 82 00:08:03,130 --> 00:08:10,480 weather data model which is able to figure out the condition name based on the condition ID, then I 82 83 00:08:10,480 --> 00:08:20,470 would need to go into my Model folder and create a New File which I'm going to call the WeatherModel, 83 84 00:08:22,370 --> 00:08:28,510 and I'm gonna use this file to model what a weather object should look like. 84 85 00:08:28,510 --> 00:08:34,690 Let's create it as a struct and it's gonna be called a WeatherModel and it's gonna have a number of 85 86 00:08:34,690 --> 00:08:35,490 properties. 86 87 00:08:35,530 --> 00:08:42,270 So we definitely need to have a conditionId property which is an Int. 87 88 00:08:42,430 --> 00:08:48,760 We also need to have a city name property which is gonna be a String. 88 89 00:08:48,760 --> 00:08:54,920 And finally, we also need a temperature property which is gonna be a Double. 89 90 00:08:54,910 --> 00:09:01,930 So now I can go ahead and move this functionality of figuring out what the condition name should be 90 91 00:09:02,290 --> 00:09:05,810 based on the condition code or the weatherId. 91 92 00:09:05,860 --> 00:09:10,740 This is very much within the realm of the weather object. 92 93 00:09:10,750 --> 00:09:13,230 So let's go ahead and paste that in here. 93 94 00:09:14,810 --> 00:09:22,130 Now, back inside our Weather Manager. Instead of having all these disparate pieces of information, I can 94 95 00:09:22,130 --> 00:09:33,080 create my ID as my decodedData weatherId. I can create my temperature or temp as the 95 96 00:09:33,230 --> 00:09:36,500 decodedData.main.temp. 96 97 00:09:36,500 --> 00:09:41,700 And finally, I'm gonna create my city name or I'm just gonna call it name for short 97 98 00:09:41,840 --> 00:09:45,560 from the decodedData.name property. 98 99 00:09:45,560 --> 00:09:53,000 So, now that I've got all the properties that I want out of my weather data, I can create myself a weather 99 100 00:09:53,090 --> 00:09:56,120 object from my new WeatherModel. 100 101 00:09:56,780 --> 00:10:02,840 So when we initialize it, we have provide the conditionId which is the ID, the cityName which is 101 102 00:10:02,930 --> 00:10:07,240 name, and the temperature which is temp. 102 103 00:10:07,590 --> 00:10:17,760 Now, if I wanted to get a hold of the ConditionName, then I can simply write weather.getConditionName 103 104 00:10:17,760 --> 00:10:28,680 and I have to pass in the ID. But wouldn't it be nice if I could just have a property in my weather 104 105 00:10:28,680 --> 00:10:35,670 object that's called a condition name and it manages to figure out what that should be. 105 106 00:10:35,880 --> 00:10:43,910 Instead of getting my weather manager to call the method getConditionName, in order to get the ID. 106 107 00:10:44,460 --> 00:10:50,850 This is a good opportunity to show you a cool thing that we can do in Swift which is called a computed 107 108 00:10:50,850 --> 00:10:51,390 property. 108 109 00:10:52,020 --> 00:10:57,960 So we've got all three properties here which are actually called stored properties because all they 109 110 00:10:57,960 --> 00:11:00,840 do is they just store pieces of data. 110 111 00:11:01,200 --> 00:11:04,710 But I can also create a computed property. 111 112 00:11:05,340 --> 00:11:13,200 So I'm going to create it using a var keyword and I'm going to call it the conditionName, and it's going 112 113 00:11:13,200 --> 00:11:21,750 to have a data type of String, and then I'm going to open up a set of parentheses. And inside the set 113 114 00:11:21,750 --> 00:11:30,060 of parentheses, I'm expected to return or output what the value for the condition name should be. 114 115 00:11:30,060 --> 00:11:36,690 That would require me to work out the condition name which, luckily, I've already got down here in the 115 116 00:11:36,690 --> 00:11:38,610 form of my Switch statement. 116 117 00:11:38,610 --> 00:11:48,990 So let's paste that in here and we can use the conditionId property value to switch, and then check 117 118 00:11:49,260 --> 00:11:55,830 what condition it is to return this string which is going to be equal to the conditionName. 118 119 00:11:56,200 --> 00:12:03,870 So, now I've got a computed property because this property condition name is going to work hard 119 120 00:12:03,870 --> 00:12:09,300 its value based on the code inside these curly braces. 120 121 00:12:09,360 --> 00:12:15,660 What this allows me to do is if I want to get hold of the weather condition, all I would have to do is 121 122 00:12:15,660 --> 00:12:23,810 say weather.conditionName and I can use it just like any other property. 122 123 00:12:24,030 --> 00:12:30,200 And if you run your app right now, you'll see it works perfectly 123 124 00:12:30,200 --> 00:12:32,030 just like before. 124 125 00:12:32,030 --> 00:12:40,340 But our code is now a lot better after the refactoring and it's a lot more succinct and easier to understand. 125 126 00:12:40,340 --> 00:12:44,360 So what's a general format for computed properties in Swift? 126 127 00:12:44,360 --> 00:12:49,420 Let's go over the syntax for computed properties again. In order to create a computed property, 127 128 00:12:49,430 --> 00:12:55,550 it always has to be a var because the value is always going to change. It's going to be based on the 128 129 00:12:55,550 --> 00:12:57,000 computation. 129 130 00:12:57,290 --> 00:12:58,810 And then we give it a name, 130 131 00:12:58,820 --> 00:13:04,630 so let's call it aProperty. And then we have to give it a data type, 131 132 00:13:04,670 --> 00:13:07,550 so let's say I want it to be an integer. 132 133 00:13:07,550 --> 00:13:14,660 Now, in between the curly braces, I have to provide an output that is going to be set as the value of 133 134 00:13:14,660 --> 00:13:15,830 this property. 134 135 00:13:15,890 --> 00:13:20,810 So let's say that I wanted to calculate, I don't know, 2 + 5, 135 136 00:13:21,380 --> 00:13:23,380 So this is the computation 136 137 00:13:23,600 --> 00:13:28,730 and that means aProperty is going to be equal to the result of this computation which is going to be 137 138 00:13:28,730 --> 00:13:29,920 7. 138 139 00:13:30,320 --> 00:13:32,870 That's all computed property in Swift. 139 140 00:13:33,170 --> 00:13:41,990 And as practice and as a challenge, I want you to figure out how we can get hold of the temperature as 140 141 00:13:42,050 --> 00:13:46,160 a string with only one decimal place. 141 142 00:13:46,160 --> 00:13:49,740 So you've done this before using the string initializer, 142 143 00:13:50,000 --> 00:13:57,190 but I want you to turn that into a computed property now, and it should be called temperatureString. 143 144 00:13:57,470 --> 00:14:03,410 And if you manage to successfully solve the challenge, then you should be able to print 144 145 00:14:03,590 --> 00:14:12,790 weather.temperatureString. And when you run the app and check the weather, then you should get the temperature 145 146 00:14:12,790 --> 00:14:20,130 back in a string format with only a single number after the decimal place. 146 147 00:14:20,350 --> 00:14:26,920 So pause the video and see if you can complete this challenge. 147 148 00:14:27,310 --> 00:14:27,610 All right. 148 149 00:14:27,640 --> 00:14:30,970 So we've got a temperatureString computed property here. 149 150 00:14:32,140 --> 00:14:38,650 And remember, we always have to provide a data type for the computed property, and then we open a set 150 151 00:14:38,650 --> 00:14:42,280 of curly braces and actually work out what it should be. 151 152 00:14:42,280 --> 00:14:49,370 So in this case, I'm going to return a String with the format of a single decimal place, 152 153 00:14:49,540 --> 00:14:57,550 so thats's "%.1f" and then I'm gonna pass in my temperature which is this Double right 153 154 00:14:57,550 --> 00:15:06,030 here as the property that's going to be formatted to this particular format. And now when you run your 154 155 00:15:06,030 --> 00:15:12,960 app, you should be able to see that whatever temperature you get back will be printed with only one decimal 155 156 00:15:12,960 --> 00:15:20,940 place. And this temperature string now has the data type of a String ready for us to put into our temperature 156 157 00:15:20,940 --> 00:15:22,570 label. 157 158 00:15:22,680 --> 00:15:28,980 So in the next lesson, we're going to tie it all together and get our weather app working with all of 158 159 00:15:28,980 --> 00:15:33,750 these different components together. For all of that and more, I'll see you there.