0 1 00:00:00,940 --> 00:00:08,740 So, now we need to add some code that allows us to click on one of these categories and it should take 1 2 00:00:08,740 --> 00:00:15,560 us to the TodoListViewController and populate the table view in the TodoListViewController that 2 3 00:00:15,640 --> 00:00:19,570 are all the items that are under this Shopping List category. 3 4 00:00:19,570 --> 00:00:21,790 So let's get going with that. 4 5 00:00:21,790 --> 00:00:29,590 So in order to do this, we'll need our Table View Delegate Methods and those-- and we can put that under 5 6 00:00:29,590 --> 00:00:31,040 the Datasource methods here, 6 7 00:00:31,270 --> 00:00:38,320 And the method that will be especially crucial will be tableView didSelectRowAt indexPath because 7 8 00:00:38,410 --> 00:00:41,100 this will trigger when we select one of the cells. 8 9 00:00:41,140 --> 00:00:47,680 So when this delegate method triggers when we select one of the cells, we probably want to trigger that 9 10 00:00:47,680 --> 00:00:50,180 segue that we set up earlier on here 10 11 00:00:50,440 --> 00:00:55,470 that takes the user from the Category view controller to the Items view controller, 11 12 00:00:55,600 --> 00:00:58,670 and that segue we called goToItems. 12 13 00:00:58,840 --> 00:01:00,330 So that's what we're going to call here, 13 14 00:01:00,340 --> 00:01:08,080 we're going to say performSegue(withIdentifier "goToItems", senda: self). 14 15 00:01:08,080 --> 00:01:13,600 But before we trigger the segue., we need to do a little bit of prep for the next view controller because 15 16 00:01:13,690 --> 00:01:17,780 right now this TodoListViewController that when it gets loaded up, 16 17 00:01:17,830 --> 00:01:22,240 it's just going to pull out all of the items that we've ever saved which doesn't achieve what we're 17 18 00:01:22,240 --> 00:01:22,900 trying to do. 18 19 00:01:22,900 --> 00:01:26,910 So in order to do that, we need to initialize this 19 20 00:01:26,920 --> 00:01:34,270 itemArray with all of the items that belong to the category that was selected. 20 21 00:01:34,270 --> 00:01:37,900 So as always, we need to do the prep work inside the delegate method 21 22 00:01:37,900 --> 00:01:44,720 prepare for segue, because this is going to be triggered just before we perform that segue. 22 23 00:01:44,860 --> 00:01:50,960 So here, we're going to create a new constant that is going to store a reference to the destination 23 24 00:01:51,040 --> 00:01:57,940 view controller and we can grab this by using the segue.destination property, and we're going to 24 25 00:01:57,940 --> 00:02:04,330 downcast this as a TodoListViewController because we know where that segue is going to take us 25 26 00:02:04,330 --> 00:02:04,690 to. 26 27 00:02:04,840 --> 00:02:10,480 And if we had more than one segue originating from our Category View Controller, then we might also 27 28 00:02:10,510 --> 00:02:15,550 add an if statement just to check if the segue has identified goToItems, 28 29 00:02:15,550 --> 00:02:16,700 then do this. 29 30 00:02:16,750 --> 00:02:20,670 But in our case, we've only got one segue, so I'm going to shortcut that for now. 30 31 00:02:20,740 --> 00:02:28,630 So the next thing that we need to do is to grab the category that corresponds to the selected cell. 31 32 00:02:28,870 --> 00:02:31,890 And to do that, I first need to know what is the selected cell. 32 33 00:02:32,060 --> 00:02:39,940 While I have access to that inside the tableView didSelectRowAt indexPath delegate method, I can tap into 33 34 00:02:39,940 --> 00:02:44,240 that local variable inside the prepare for segue delegate method. 34 35 00:02:44,470 --> 00:02:55,900 But I can say let indexPath = tableView.indexPath for selected row, and this is the indexPath 35 36 00:02:55,900 --> 00:03:01,390 that is going to identify the current row that is selected. 36 37 00:03:01,420 --> 00:03:07,840 But this if you option click on it, you can see is actually an optional index path because it might 37 38 00:03:07,840 --> 00:03:11,850 be that at this point, there's actually no road that's selected. 38 39 00:03:11,860 --> 00:03:18,150 Now, that's not likely to be possible because we're only triggering our segue when we do select a row. 39 40 00:03:18,400 --> 00:03:20,970 But just in case, let's wrap that inside 40 41 00:03:21,010 --> 00:03:29,280 an "if-let" and if the index path is not nil, then we're going to tap into the destination view controller 41 42 00:03:29,650 --> 00:03:36,400 and we're going to set a property--and we're going to set a property in the destination view controller 42 43 00:03:36,730 --> 00:03:39,130 called selectedCategory. 43 44 00:03:39,130 --> 00:03:43,560 Now, this does not yet exist, but we're going to create it very shortly. 44 45 00:03:43,780 --> 00:03:46,960 So Xcode is going to complain, but it's okay, we know why. 45 46 00:03:47,170 --> 00:03:56,620 But we're going to set this selected category to our array of categories at our indexPath, not the one 46 47 00:03:56,620 --> 00:03:57,650 with the "I," 47 48 00:03:57,670 --> 00:04:02,090 but the one that's lower case that we created here. 48 49 00:04:02,110 --> 00:04:03,440 .row. 49 50 00:04:03,460 --> 00:04:10,030 So, now in order for our code to work, we need to create this property called selected category inside 50 51 00:04:10,090 --> 00:04:12,180 our TodoListViewController. 51 52 00:04:12,220 --> 00:04:18,160 So just under our itemArray, I'm going to create a new variable called selectedCategor 52 53 00:04:18,270 --> 00:04:24,640 and this is going to have a data type of an optional category because it's going to be nil until we 53 54 00:04:24,640 --> 00:04:27,100 can set it using this line of code. 54 55 00:04:27,190 --> 00:04:34,330 But once I do set that selected category, then that's the time point when I want to load up all the items 55 56 00:04:34,690 --> 00:04:36,920 that are relevant to this category. 56 57 00:04:37,210 --> 00:04:44,470 So something that I can do in Swift is I can open up a set of brackets after the select category variable 57 58 00:04:44,950 --> 00:04:53,710 and I can use a special keyword called didSet. And everything that's between these curly braces is going 58 59 00:04:53,710 --> 00:05:00,110 to happen as soon as selected category gets set with a value. 59 60 00:05:00,380 --> 00:05:07,150 And this is the perfect place to call loadItems and we can delete it from our 60 61 00:05:07,150 --> 00:05:07,940 viewDidLoad. 61 62 00:05:07,940 --> 00:05:14,920 So that means when we do call loadItem, we're certain that we've already got a value for our selected 62 63 00:05:14,930 --> 00:05:20,300 category, and we're not calling it before we actually have a value which might crash our app. 63 64 00:05:20,300 --> 00:05:27,770 So, now if I turn this icon from gray to white by hitting command S and saving it, then if I head back 64 65 00:05:27,860 --> 00:05:36,830 to my CategoryViewController and hit command B to hit Xcode with a stick and refresh this code, then the error 65 66 00:05:36,860 --> 00:05:42,920 should go away. Now, Xcode is notoriously buggy, especially given how long Apple's been working on 66 67 00:05:42,920 --> 00:05:47,270 it, and sometimes it just doesn't work as smoothly as you wanted to. 67 68 00:05:47,450 --> 00:05:49,480 But we have gotten rid of all of our errors. 68 69 00:05:49,490 --> 00:05:56,210 Now, there's just one last thing that we need to do before we can run and test our app and that's inside 69 70 00:05:56,210 --> 00:05:59,310 our TodoListViewController. 70 71 00:05:59,330 --> 00:06:07,040 In our addButtonPressed IBAction, at the point where we create a new item, now not only do we have to specify 71 72 00:06:07,040 --> 00:06:08,750 the title of the item, 72 73 00:06:08,930 --> 00:06:17,480 whether it was done, but we now also have to specify its parentCategory. And this property 73 74 00:06:17,480 --> 00:06:25,930 parentCategory is available to us because we created that relationship, that points to the category entity. 74 75 00:06:26,060 --> 00:06:32,120 And because we've already set the selected category that is pairing the current 75 76 00:06:32,120 --> 00:06:32,510 TodoListViewController, 76 77 00:06:32,720 --> 00:06:38,450 then we can just simply say newItem.parentCategory = selectedCategory. 77 78 00:06:38,600 --> 00:06:44,000 And, of course, because we're inside a closure, we just need to add the word "self" in front. 78 79 00:06:44,030 --> 00:06:47,140 So let's give it a go and run our app. 79 80 00:06:47,150 --> 00:06:47,480 All right. 80 81 00:06:47,480 --> 00:06:50,910 So here's all of those categories that we created earlier on. 81 82 00:06:51,140 --> 00:06:56,390 And I'm going to select one of these categories and I want you to predict what is going to happen when 82 83 00:06:56,390 --> 00:07:00,500 I click on this. So was this what you expected? 83 84 00:07:00,730 --> 00:07:08,140 We have loaded up all of the previous items that we have saved into our to-do list before we even established 84 85 00:07:08,140 --> 00:07:10,630 the relationships or the categories. 85 86 00:07:10,690 --> 00:07:17,210 And in fact, when we created any of these items, none of them actually had a parent category associated. 86 87 00:07:17,230 --> 00:07:19,260 So why is this happening? 87 88 00:07:19,390 --> 00:07:21,760 Well, this is a bug and we need to solve it. 88 89 00:07:21,790 --> 00:07:27,550 If you think about it, we need to figure out how is our TodoListViewController loading up all the 89 90 00:07:27,550 --> 00:07:29,110 items in the table view. 90 91 00:07:29,350 --> 00:07:31,900 Well, the items come from the itemArray, 91 92 00:07:31,900 --> 00:07:32,680 right? 92 93 00:07:32,710 --> 00:07:40,840 And the itemArray comes from the loadItems method which simply fetches all of the NSManagedObjects 93 94 00:07:40,990 --> 00:07:44,260 that belong in the item table or item entity. 94 95 00:07:44,440 --> 00:07:51,870 But in order for us to only load the items that have the parentCategory matching the selectedCategory, 95 96 00:07:52,240 --> 00:07:56,570 we need to query our database for it, and we need to filter the result. 96 97 00:07:56,590 --> 00:08:04,570 So we need to basically create a predicate that is a NSPredicate and initialize it with the format 97 98 00:08:04,840 --> 00:08:14,700 that the parentCategory of all of the items that we want back must have its name property matching the 98 99 00:08:14,700 --> 00:08:17,430 current selectedCategor.name. 99 100 00:08:17,490 --> 00:08:24,190 And then we need to add this predicate to the request, request.predicate = predicate. 100 101 00:08:24,360 --> 00:08:31,080 But now we have a bit of a problem because if we set the predicate here and we set the predicate property 101 102 00:08:31,110 --> 00:08:38,820 on the request to this new predicate, then the ones that we set before, say, for example, in our searchBar 102 103 00:08:38,820 --> 00:08:44,220 is not going to be valid anymore and it's going to be overwritten. And you can see this if I run 103 104 00:08:44,520 --> 00:08:46,070 our app in its current state. 104 105 00:08:46,080 --> 00:08:52,740 So if we hit Shopping List, we do, in fact, go into an empty list because we haven't yet created any new 105 106 00:08:52,770 --> 00:08:57,240 items that have the parentCategory as Shopping List. 106 107 00:08:57,300 --> 00:09:09,270 But if we add a few items and we now try to search these items, you can see that our search fails and 107 108 00:09:09,300 --> 00:09:14,690 all we get are just the same items, but now they're sorted in alphabetical order. 108 109 00:09:14,880 --> 00:09:19,940 And the reason is because when we press the search button, we create a request, 109 110 00:09:20,190 --> 00:09:21,850 we add the filter 110 111 00:09:21,880 --> 00:09:23,770 or how we want to query our data, 111 112 00:09:24,000 --> 00:09:29,490 and then we say that we want to sort it in alphabetical order, and then we run loadItems using this 112 113 00:09:29,490 --> 00:09:30,400 current request. 113 114 00:09:30,540 --> 00:09:38,850 But once that request reaches here, then we override the predicate using a new predicate which is simply 114 115 00:09:38,850 --> 00:09:44,870 that the parentCategory must match the current selectedCategory which is not very useful. 115 116 00:09:44,910 --> 00:09:50,850 While we do want every single item that's loaded up in the table to have a parentCategory that is the 116 117 00:09:50,850 --> 00:09:55,500 current selected category, we also want our search to work. 117 118 00:09:55,500 --> 00:09:57,390 So how can we fix this? 118 119 00:09:57,420 --> 00:09:59,540 Now, this is almost a bit like a brainteaser. 119 120 00:09:59,550 --> 00:10:04,440 But you should already have most of the components 120 121 00:10:04,770 --> 00:10:08,220 and with a light bit of Googling, it shouldn't be too difficult to fix this. 121 122 00:10:08,250 --> 00:10:12,360 So if you want to take this optional challenge, then this is a good time to do it. 122 123 00:10:12,450 --> 00:10:17,070 Now, if you don't, then I'm going to run through how we can fix this. 123 124 00:10:17,100 --> 00:10:25,350 So what if we add another parameter to our loadItems and it's going to be the predicate or whatever search 124 125 00:10:25,350 --> 00:10:28,830 query we want to make in order to load up our items. 125 126 00:10:28,830 --> 00:10:31,560 So this is going to be of data type and as predicate. 126 127 00:10:31,620 --> 00:10:39,120 So that means that we can create request and we can create predicates, and then we can say load Items 127 128 00:10:39,300 --> 00:10:48,310 with request and the predicate being this format, i.e., the title of the to-do list item must contain 128 129 00:10:48,310 --> 00:10:50,570 what's inside the current search bar, 129 130 00:10:50,680 --> 00:10:55,900 then we can come over here and we can create a compound predicate. 130 131 00:10:55,930 --> 00:11:03,700 So this is going to be set to an NSCompoundPredicate and we're going to initialize it using 131 132 00:11:03,700 --> 00:11:12,280 Subpredicates which include the predicate that we get passed in as an argument, and also the category predicate 132 133 00:11:12,610 --> 00:11:18,730 which makes sure that we filter and keep only the items where the parentCategory matches the current 133 134 00:11:18,730 --> 00:11:20,110 selectedCategory. 134 135 00:11:20,140 --> 00:11:27,810 So then we have an array that contains the categoryPredicate and the predicate that gets passed in. 135 136 00:11:27,940 --> 00:11:34,120 And instead of setting the request of predicate to simply a single predicate, we can set it to the compound 136 137 00:11:34,120 --> 00:11:34,810 predicate. 137 138 00:11:34,810 --> 00:11:40,130 So, now there's just one last problem, namely these errors that we've got, 138 139 00:11:40,270 --> 00:11:49,420 because loadItems while we have a default value for the request, we don't yet have a default value for 139 140 00:11:49,420 --> 00:11:50,250 the predicate. 140 141 00:11:50,290 --> 00:11:54,410 So it's expecting that predicate argument in our method call. 141 142 00:11:54,490 --> 00:12:02,110 But in both of these cases, all we want is to load up all of the items that fit the current selectedCategory, 142 143 00:12:02,110 --> 00:12:10,080 so we can also set this predicate to have a default value of nil, and we can turn this 143 144 00:12:10,120 --> 00:12:13,010 NSPredicate into an optional. 144 145 00:12:13,030 --> 00:12:20,620 So that means that we can still call this loadItems without providing any parameters because both parameter 145 146 00:12:20,710 --> 00:12:22,720 have a default value. 146 147 00:12:22,720 --> 00:12:29,830 Now, we just need to make sure that we create our compound predicate using both the category predicate 147 148 00:12:30,220 --> 00:12:33,690 as well as the one that's passed in through as an argument 148 149 00:12:33,790 --> 00:12:40,230 if this is not nil. So we can rewrite these two lines using optional binding to make sure that we're 149 150 00:12:40,240 --> 00:12:41,280 always safe. 150 151 00:12:41,500 --> 00:12:47,860 So we can say if let additionalPredicate = predicate, 151 152 00:12:48,390 --> 00:12:50,850 so making sure that it is not nil, 152 153 00:12:50,850 --> 00:12:57,240 then we're going to say that the predicate for our request is going to be NSCompoundPredicate 153 154 00:12:57,360 --> 00:13:07,260 created using the Subpredicates, namely the categoryPredicate, and the new unwrapped additionalPredicate. 154 155 00:13:07,290 --> 00:13:16,230 But if this is not true, i.e., else then our request.predicate is simply going to be equal to the 155 156 00:13:16,230 --> 00:13:17,600 categoryPredicate. 156 157 00:13:17,640 --> 00:13:23,280 And now, we successfully replace these two lines of code using optional binding and making sure that 157 158 00:13:23,280 --> 00:13:25,340 whenever unwrapping a nil value. 158 159 00:13:25,470 --> 00:13:30,330 So, now let's try our app out and see if it works as expected. 159 160 00:13:30,510 --> 00:13:38,670 So I'm, again, going to go into the Shopping List to-do list and I'm going to search for "Apples" and when 160 161 00:13:38,670 --> 00:13:46,350 I hit search, all I get are Apples, and when I click on the cross, then I go back to the original to-do 161 162 00:13:46,350 --> 00:13:46,670 list. 162 163 00:13:46,680 --> 00:13:53,130 So we are introducing a lot of new concepts and it might take a little while for it to sink in, but you 163 164 00:13:53,130 --> 00:14:00,020 can help it by going back and reviewing some of these new concepts that we've talked about in this module. 164 165 00:14:00,060 --> 00:14:07,450 So those new concepts include using extensions to separate out bits of functionality inside your view 165 166 00:14:07,500 --> 00:14:16,170 controller, using the didSet to specify what should happen when a variable gets set with a new value, 166 167 00:14:16,200 --> 00:14:23,880 how can we create, update, read, and destroy data using our context, and how we can commit the current state 167 168 00:14:23,880 --> 00:14:28,420 of the context to our persistent container using context.safe, 168 169 00:14:28,830 --> 00:14:35,970 how we were able to access a singleton that's the shared application and get its delegate property as 169 170 00:14:35,970 --> 00:14:42,180 an app delegate in order to tap into this persistent container lazy variable. 170 171 00:14:42,180 --> 00:14:48,000 So if you want to understand any new concept, you have to put it into practice. 171 172 00:14:48,090 --> 00:14:55,020 So this is a good time to create your own app using the concepts that we've learned and that I've demonstrated 172 173 00:14:55,470 --> 00:14:56,560 inside this app. 173 174 00:14:56,640 --> 00:15:04,170 So it might be that you want to create a notepad like app that persists everything that you save into 174 175 00:15:04,170 --> 00:15:06,590 your notepad using Core Data. 175 176 00:15:06,810 --> 00:15:13,260 But no matter what you do, it's really, really important that you try these concepts out by yourself and 176 177 00:15:13,260 --> 00:15:18,870 struggle with it a little bit until you make it your own knowledge and you finally understand it. Because 177 178 00:15:18,870 --> 00:15:22,590 this is not something that you're going to be able to just passively soak in, 178 179 00:15:22,590 --> 00:15:24,830 it is complex and it is difficult, 179 180 00:15:25,050 --> 00:15:28,130 so it needs practice and it needs repetition, 180 181 00:15:28,140 --> 00:15:31,410 so enough mastery wisdom. In the next lesson, 181 182 00:15:31,530 --> 00:15:39,000 we're going to look at how we can use this open-source library called Realm to do everything that we've 182 183 00:15:39,000 --> 00:15:45,870 done so far using Core Data, but with far less code, and create a persistent relational database that is 183 184 00:15:45,870 --> 00:15:49,100 far more efficient than just by using Core Data. 184 185 00:15:49,470 --> 00:15:51,340 So for all of that and more, 185 186 00:15:51,450 --> 00:15:53,300 again, I'll see you on the next lesson.