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