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.
