0
1
00:00:00,750 --> 00:00:07,400
So in the last lesson, we looked at how to perform create in CRUD using Realm,
1

2
00:00:07,590 --> 00:00:13,730
and we were able to successfully add new categories that we created inside
2

3
00:00:13,770 --> 00:00:19,840
our addButtonPressed IBAction and save it to our Realm persistent data store.
3

4
00:00:20,190 --> 00:00:25,860
Now, in this lesson, we're going to tackle the next letter which is read inside CRUD and we're going to
4

5
00:00:25,860 --> 00:00:32,760
look at how we can load up our categories from all Realm database, rather than from Core Data.
5

6
00:00:32,790 --> 00:00:37,380
And so the first thing I'm going to do is I'm going to delete this thing called context because we no
6

7
00:00:37,380 --> 00:00:38,420
longer need it.
7

8
00:00:38,520 --> 00:00:45,340
So the place where we read from our database is inside our loadCategories method.
8

9
00:00:45,360 --> 00:00:49,530
So if we scroll down to find it, you can see that we've commented all out.
9

10
00:00:49,770 --> 00:00:58,110
So instead of having this request which is a fetch request or try context.fetch, we can replace
10

11
00:00:58,260 --> 00:01:01,920
all of this code with just a single line.
11

12
00:01:01,980 --> 00:01:12,000
We can say let categoryies = realm.objects, and then we just specify the type which is all of the
12

13
00:01:12,000 --> 00:01:21,870
category type objects. And this will pull out all of the items inside our realm that are of Category
13

14
00:01:22,140 --> 00:01:23,110
objects.
14

15
00:01:23,310 --> 00:01:31,460
And that means that we can delete all of these lines of Core Data code replaced with just this one line.
15

16
00:01:31,530 --> 00:01:34,560
Now, at the moment, I'm creating it as a local variable.
16

17
00:01:34,740 --> 00:01:39,630
But if we wanted to use it elsewhere, then we can use our variable categories.
17

18
00:01:39,630 --> 00:01:45,570
Now, what you'll see is that I can't straight up just assign it to this variable called categories,
18

19
00:01:45,570 --> 00:01:51,750
it's going to give me an error. And the error it says is that it can't assign a value of type results
19

20
00:01:51,840 --> 00:01:53,790
to type array.
20

21
00:01:53,790 --> 00:02:02,660
So what it's saying is that the data type of the objects that we get back is results which is a container
21

22
00:02:02,660 --> 00:02:06,400
type that comes from Realm Swift.
22

23
00:02:06,420 --> 00:02:14,100
So this is equivalent to list or array, but it's a different data type and it's not very easily converted.
23

24
00:02:14,400 --> 00:02:22,230
But what we can do is we can change the data type of our Category's property to the results data type.
24

25
00:02:23,100 --> 00:02:30,270
So we can say that categories is of data type Results, and Results is basically just an auto-updating
25

26
00:02:30,270 --> 00:02:31,280
container type,
26

27
00:02:31,350 --> 00:02:38,310
so container types are things like arrays, dictionary, results, lists, and it comes from Realm and it gets
27

28
00:02:38,310 --> 00:02:40,910
returned from object queries.
28

29
00:02:40,920 --> 00:02:47,730
So that means that whenever you try to query your Realm database, the results you get back is in the form of
29

30
00:02:47,730 --> 00:02:49,400
a Results object.
30

31
00:02:49,470 --> 00:02:55,260
So we can specify our categories, which is what we're going to get back when we query our database, as
31

32
00:02:55,260 --> 00:03:02,580
data type of results, and it's going to contain a whole bunch of Category objects. And I'm going to first
32

33
00:03:02,730 --> 00:03:05,040
unwrapped that and we'll go with that for now.
33

34
00:03:05,040 --> 00:03:15,000
So now this line of code is perfectly valid because the data type that we get back here is Results containing
34

35
00:03:15,360 --> 00:03:22,020
code bunch of categories as the element inside the results, and then categories is also the same data
35

36
00:03:22,020 --> 00:03:28,920
type. So that matches and that's now allowing us to proceed. But we get this error down here where we say
36

37
00:03:28,920 --> 00:03:32,670
self.categories.append newCategory.
37

38
00:03:32,670 --> 00:03:39,870
Now, because this result data type, as we found out earlier on, is an auto-updating container.
38

39
00:03:40,020 --> 00:03:44,080
We don't actually need to append things to it anymore.
39

40
00:03:44,370 --> 00:03:48,300
It will simply auto-update and monitor for those changes.
40

41
00:03:48,480 --> 00:03:56,040
So, now with just one line, we've managed to place four or five lines of Core Data code that does exactly
41

42
00:03:56,040 --> 00:03:57,390
the same thing.
42

43
00:03:57,510 --> 00:04:01,320
So let's give it a go and run our app and see if it works.
43

44
00:04:01,320 --> 00:04:02,530
So there you go.
44

45
00:04:02,550 --> 00:04:09,460
Those are our two items that we saved earlier on into our Realm Browser as you can see here.
45

46
00:04:09,630 --> 00:04:18,960
And if I terminate the app and go back into it, you can see that it's still able to persist and load
46

47
00:04:18,960 --> 00:04:24,420
up or read from our Realm database just based on those two lines of code.
47

48
00:04:24,420 --> 00:04:29,550
Now, let's address the issue of force unwrapping this particular property.
48

49
00:04:29,850 --> 00:04:34,440
It's not ideal because what if, say, we forgot to load up our categories,
49

50
00:04:34,440 --> 00:04:42,490
and this was, in fact, nil when we force unwrap it and try to use it to populate our table view.
50

51
00:04:42,930 --> 00:04:49,130
Well, then we're going to have an issue, and you're going to see it when I run our app. There we go. "Thread 1:
51

52
00:04:49,140 --> 00:04:55,770
Fatal error: Unexpectedly found nil while unwrapping..." this particular optional.
52

53
00:04:55,770 --> 00:04:59,340
So let's stop our app and let's make our code safer.
53

54
00:04:59,400 --> 00:05:06,490
So instead of making it a forced unwrapped, I'm going to add a question mark to this data type and make
54

55
00:05:06,490 --> 00:05:08,710
categories an optional.
55

56
00:05:08,920 --> 00:05:16,210
So, now instead of saying categories. count, I'm going to say if categories is not nil, then return
56

57
00:05:16,270 --> 00:05:17,820
categories?.count.
57

58
00:05:18,190 --> 00:05:23,410
But if it is nil, then just return 1.
58

59
00:05:23,440 --> 00:05:31,110
So this is a new bit of syntax. And this in Swift is called the Nil Coalescing Operator.
59

60
00:05:31,540 --> 00:05:35,290
What it means is that this could be nil.
60

61
00:05:35,560 --> 00:05:41,390
Because categoreis is an optional and we're only saying get the count of categories
61

62
00:05:41,620 --> 00:05:45,470
if it is not nil, but if it is nil, then this is going to be nil, right?
62

63
00:05:45,470 --> 00:05:54,010
But if that happens, then the nil coalescing operator will say then use this value instead.
63

64
00:05:54,340 --> 00:06:01,200
So if categories is not nil, then we'll return the number of categories we have.
64

65
00:06:01,660 --> 00:06:05,630
But if it is nil, then we're going to just return 1,
65

66
00:06:05,710 --> 00:06:16,320
so our table view will have just one row. And down here if categories, the optional is not nil,
66

67
00:06:16,540 --> 00:06:22,370
then we're going to get the item at the indexPath.row and we're going to grab the name property.
67

68
00:06:22,660 --> 00:06:31,960
But if it is nil, then we're simply going to say the text is just going to be equal to "No Categories
68

69
00:06:32,350 --> 00:06:33,880
Added yet."
69

70
00:06:33,880 --> 00:06:39,670
And finally, I'm going to add a question mark here to say that the selected category of the destinationVC
70

71
00:06:39,670 --> 00:06:42,630
will be set to the category
71

72
00:06:42,640 --> 00:06:50,410
if it's not nil at the indexPath.row that we selected. And that gets passed into here as the
72

73
00:06:50,410 --> 00:06:56,160
selectedCategory which is an optional category. And only when it is set,
73

74
00:06:56,230 --> 00:06:57,410
i.e., it's not nil,
74

75
00:06:57,490 --> 00:06:59,070
do we actually call load items.
75

76
00:06:59,110 --> 00:07:01,390
So we're still okay over here.
76

77
00:07:01,390 --> 00:07:09,790
So, now even though my loadCategories is still not commented in because it's not active and this categories
77

78
00:07:09,790 --> 00:07:16,000
will be nil when we try to run our app, you'll see that our app no longer crashes and burns. And instead, it
78

79
00:07:16,000 --> 00:07:20,590
actually has a slightly different but better user experience.
79

80
00:07:20,590 --> 00:07:29,050
So, now you can see that the app has not crashed even though this categories property is now nil.
80

81
00:07:29,050 --> 00:07:35,410
And that's because it has some default values. And the default value when categories is nil is to produce
81

82
00:07:35,410 --> 00:07:40,990
a single table view cell and for that cell to have the text of "No Categories Added yet."
82

83
00:07:41,140 --> 00:07:46,480
And that makes our app so much safer and a much better user experience.
83

84
00:07:46,480 --> 00:07:48,990
Now, the Add button is still going to break
84

85
00:07:49,060 --> 00:07:54,480
when we try to add new categories, and it's not going to show up.
85

86
00:07:54,520 --> 00:08:01,780
And that's because we need this loadCategories method to be called so that we start auto-updating our
86

87
00:08:01,780 --> 00:08:05,350
categories using the objects inside our realm.
87

88
00:08:05,530 --> 00:08:14,020
So let's go back and uncomment this method call and run our app again, and you'll see that we still have
88

89
00:08:14,110 --> 00:08:17,740
all of our items showing up and persisting.
89

90
00:08:17,740 --> 00:08:26,980
So we've now managed to use our Realm database to add new items and save them to our Realm, and we've
90

91
00:08:27,040 --> 00:08:34,150
also managed to use it to load up our Realm objects using a single line of code.
91

92
00:08:34,240 --> 00:08:40,330
And now all we have to do is to fix the same things inside our TodoListViewController.
92

93
00:08:40,360 --> 00:08:47,290
Now, all we have to do is to fix the same things inside our TodoListViewController. So we are passing
93

94
00:08:47,290 --> 00:08:56,530
over the current category as a optional category and it's going to land over here as the selectedCategory
94

95
00:08:57,220 --> 00:09:04,270
And when that happens or when that does get set, then we're going to call this method called loadItems,
95

96
00:09:05,050 --> 00:09:07,480
and we no longer need our context,
96

97
00:09:07,510 --> 00:09:11,760
so we're going to delete it, and we also no longer need Core Data.
97

98
00:09:11,800 --> 00:09:17,860
So I'm going to instead import Realm Swift. And, of course, because we're using Realm, we have to create a
98

99
00:09:17,860 --> 00:09:23,730
new instance of Realm, let Realm = try! Realm.
99

100
00:09:23,740 --> 00:09:31,150
So, now when our selectedCategory does didSet from our CategoriesViewController coming over from
100

101
00:09:31,150 --> 00:09:38,440
the segue, then we're going to call loadItems. Now, loadItems is an unresolved identifier because we
101

102
00:09:38,440 --> 00:09:39,940
commented out before
102

103
00:09:39,940 --> 00:09:44,150
in order for our app to be able to run without any errors so that we can fix it now.
103

104
00:09:44,390 --> 00:09:52,900
So I'm going to uncomment this method. And inside here, I'm going to delete all of this and all of this,
104

105
00:09:53,170 --> 00:10:01,040
and instead of all of those lines of complicated Core Data code, I'm, instead, just going to say itemArray
105

106
00:10:01,520 --> 00:10:07,990
= selectedCategory.items.
106

107
00:10:08,060 --> 00:10:10,620
Remember that relationship that we're going to use now?
107

108
00:10:10,910 --> 00:10:18,860
So all of the items that belong to the current selectedCategory and we're going to sort it by KeyPath
108

109
00:10:19,010 --> 00:10:22,760
and ascending, and the KeyPath is just as we've done before,
109

110
00:10:22,760 --> 00:10:27,460
sorted by the title of each item and we're going to make it ascending: true.
110

111
00:10:27,470 --> 00:10:33,980
So KeyPath is called "title," so that's the title of each of the items that come back into the itemArray,
111

112
00:10:33,980 --> 00:10:42,620
and ascending is going to be true. And because itemArray at the moment is a array of item objects,
112

113
00:10:42,980 --> 00:10:49,700
we need to change the declaration of the tub to be this results container containing item objects data
113

114
00:10:49,700 --> 00:10:50,370
type.
114

115
00:10:50,450 --> 00:10:52,930
So let's do the same as we did before
115

116
00:10:53,120 --> 00:10:54,280
and delete that,
116

117
00:10:54,440 --> 00:11:01,310
instead declare its data type as Results, the auto-updating container containing a whole bunch of item
117

118
00:11:01,310 --> 00:11:02,010
objects.
118

119
00:11:02,130 --> 00:11:03,670
I'm going to make it an optional.
119

120
00:11:03,680 --> 00:11:15,840
So, now we can replace all of this code with just a single line like this. Now, it no longer really makes
120

121
00:11:15,840 --> 00:11:20,910
sense for this to be called iitemArray because it's now no longer an array,
121

122
00:11:20,910 --> 00:11:21,540
right?
122

123
00:11:21,540 --> 00:11:25,880
It's actually a Results container containing a whole bunch of items.
123

124
00:11:26,100 --> 00:11:30,270
So we probably should change the name of this variable.
124

125
00:11:30,330 --> 00:11:36,200
Now, I can either go around finding all the places where I used itemArray, itemArray, itemArray.
125

126
00:11:36,210 --> 00:11:42,210
In fact, the errors actually help to guide me all the places where I need to change its name. But a slightly
126

127
00:11:42,210 --> 00:11:49,020
simpler way of doing this is you can simply hit command F to find all the places where itemArray exists
127

128
00:11:49,470 --> 00:11:52,280
and you can replace it with a new name,
128

129
00:11:52,350 --> 00:11:58,350
for example, something like todoitems. But this is still a little bit clunky, it actually gives you a
129

130
00:11:58,350 --> 00:12:04,410
really, really nice way of being able to rename yourvariables and not have to worry about changing it
130

131
00:12:04,440 --> 00:12:06,080
everywhere it's used.
131

132
00:12:06,150 --> 00:12:11,370
Now, you might get some errors if you tried to do it just like this because, remember, we changed the data
132

133
00:12:11,370 --> 00:12:17,760
type of this variable and in all the places where we used it, we used it in the context of an array of
133

134
00:12:17,820 --> 00:12:18,240
items.
134

135
00:12:18,240 --> 00:12:24,840
So if you get any strange behavior trying to rename, then you can actually revert this back to an array
135

136
00:12:24,930 --> 00:12:29,190
of Item objects, and you can initialize it to an empty one,
136

137
00:12:29,430 --> 00:12:33,360
and we can simply hold down command and click on item array.
137

138
00:12:33,570 --> 00:12:40,620
And if you scroll down, there's this thing called Rename and it'll go into this accordion view where
138

139
00:12:40,890 --> 00:12:48,240
it'll only show you the lines of code where you use this variable called itemArray, and you can select or deselect
139

140
00:12:48,330 --> 00:12:50,340
the ones that you want to change.
140

141
00:12:50,340 --> 00:12:56,680
So, for example, the comments are by default not included in your renaming because, as you can see, our
141

142
00:12:56,760 --> 00:13:00,810
comments come from our old code, so we can leave those alone.
142

143
00:13:01,020 --> 00:13:04,880
But all the ones highlighted in blue will get renamed as we type in here.
143

144
00:13:05,040 --> 00:13:12,420
So I'm going to call that todoItems and I'm going to hit Rename and it's now updated it in all of
144

145
00:13:12,420 --> 00:13:13,840
these locations.
145

146
00:13:13,860 --> 00:13:21,990
So, now we can change this back to our previous results containing a whole bunch of items and it's an
146

147
00:13:21,990 --> 00:13:22,640
optional
147

148
00:13:22,710 --> 00:13:27,780
and we managed to rename all of those places which used to read itemArray.
148

149
00:13:27,990 --> 00:13:34,350
So, now because todoItems is an optional, we're going to have to either unwrap it or do something about
149

150
00:13:34,350 --> 00:13:34,920
it.
150

151
00:13:34,950 --> 00:13:37,110
So we're going to do the same as we did before.
151

152
00:13:37,160 --> 00:13:43,890
We're going to use optional chaining to say if todoItems is not nil, then return the count.
152

153
00:13:43,890 --> 00:13:47,970
But if it is nil, then return 1. And down here,
153

154
00:13:47,970 --> 00:13:50,090
we'll, first, going to use optional chaining
154

155
00:13:50,100 --> 00:13:56,340
to say that if todoItems is not nil, then grab the item at indexPath.row.
155

156
00:13:56,490 --> 00:14:05,590
And if all of this works and it's not nil, then, in that case, we're going to change all of our accessories
156

157
00:14:07,070 --> 00:14:13,280
based on the item.title and the item.done properties.
157

158
00:14:13,580 --> 00:14:24,740
But if it is nil or if it fails a "else," then we're going to say cell.textLabel?.text is going
158

159
00:14:24,740 --> 00:14:29,860
to be equal to just a simple string, and it's going to be no items added,
159

160
00:14:30,110 --> 00:14:36,780
and we're going to return the cell whichever state it is in depending on this optional binding check.
160

161
00:14:36,830 --> 00:14:40,080
Now, all we have left is this error.
161

162
00:14:40,220 --> 00:14:46,310
And for now I'm just going to comment it out because in order to persist this data using Realm, we actually
162

163
00:14:46,310 --> 00:14:48,470
have to do it slightly differently than this.
163

164
00:14:48,530 --> 00:14:53,990
And we'll tackle this in the next lesson. We will learn about update in CRUD using Realm.
164

165
00:14:53,990 --> 00:14:55,640
So let's leave that for now.
165

166
00:14:55,760 --> 00:14:59,890
But we do have to change our saveItem data.
166

167
00:15:00,230 --> 00:15:10,340
So as a challenge, I want you to be able to fix these lines of code and change it to use Realm, instead
167

168
00:15:10,340 --> 00:15:13,450
of using Core Data, as we are here.
168

169
00:15:13,520 --> 00:15:19,360
Now, when you complete this challenge, I want you to ignore this function called saveItem.
169

170
00:15:19,370 --> 00:15:21,530
In fact, just go ahead and delete it.
170

171
00:15:21,830 --> 00:15:28,080
And I want you to do all of your saving just inside this closure.
171

172
00:15:28,100 --> 00:15:35,470
So I want you to create a newItem from our data model item and I want you to save it here as well.
172

173
00:15:35,510 --> 00:15:38,050
So pause the video and give it a go.
173

174
00:15:39,250 --> 00:15:39,580
All right.
174

175
00:15:39,580 --> 00:15:41,150
So how did that go?
175

176
00:15:41,440 --> 00:15:48,910
Let's go ahead and uncomment these few lines and we're going to update it in order to use Realm. So, we
176

177
00:15:48,910 --> 00:15:50,220
no longer have a context,
177

178
00:15:50,260 --> 00:15:57,610
so let's delete that. We're just initializing a new object of the item class. And the title is going to
178

179
00:15:57,610 --> 00:16:04,300
be the current text that's inside the textField. Done doesn't need to be specified anymore because it
179

180
00:16:04,300 --> 00:16:07,490
is already here as a default.
180

181
00:16:07,720 --> 00:16:16,090
Now, because our selected category is of data type optional Category, we have to somehow unwrap it or
181

182
00:16:16,090 --> 00:16:19,380
use optional binding in order to make it work.
182

183
00:16:19,390 --> 00:16:34,290
So if we say if let current category = self.selectedCategory, and if that's not nil, then
183

184
00:16:34,380 --> 00:16:42,900
we're going to say let newItem = Item, and newItem.title is the textField.text, and instead of setting
184

185
00:16:42,900 --> 00:16:47,220
the newItem's parentCategory property here,
185

186
00:16:47,400 --> 00:16:49,860
instead, we're going to go the other direction.
186

187
00:16:49,860 --> 00:16:54,160
We're going to tap into the items that belong to the current category
187

188
00:16:54,180 --> 00:17:01,920
and we're going to append this newItem to that list. so we can say currentCategory.items, which is a
188

189
00:17:01,920 --> 00:17:07,330
list of item objects, .append this newItem.
189

190
00:17:07,710 --> 00:17:11,660
And that takes care of these two lines
190

191
00:17:12,580 --> 00:17:19,670
and replaces them. Now, as we said, instead of calling self.saveItems to do our saving down here,
191

192
00:17:19,900 --> 00:17:21,800
we're going to do all of it up here.
192

193
00:17:22,090 --> 00:17:26,200
And so we need to be able to say
193

194
00:17:26,260 --> 00:17:27,140
try realm.write,
194

195
00:17:27,610 --> 00:17:31,670
And we'll going to write is all of this.
195

196
00:17:31,840 --> 00:17:39,640
So create a newItem, give the newItem a title, and appended to the items in the currentCategory.
196

197
00:17:39,640 --> 00:17:47,620
And this, of course,, has to happen after we've unwrapped our self.selectCategory, so it needs to
197

198
00:17:47,620 --> 00:17:48,640
go in here.
198

199
00:17:48,910 --> 00:17:54,300
And because this can throw, then we need to wrap it inside a do-catch block.
199

200
00:18:01,550 --> 00:18:07,600
And because we're inside a closure, we'll use self.realm.write,
200

201
00:18:07,700 --> 00:18:11,150
and we can delete this call to save items.
201

202
00:18:11,300 --> 00:18:13,550
So did you manage to get that?
202

203
00:18:13,670 --> 00:18:20,270
It's a little bit convoluted in terms of the logic, but you can clearly see that this is far less code.
203

204
00:18:20,690 --> 00:18:27,710
And to me, it's way more human readable and the logic is easier to follow than using Core Data.
204

205
00:18:27,800 --> 00:18:34,550
And the last thing we need to do here is that once we have written this new data to all Realm, we need
205

206
00:18:34,550 --> 00:18:42,440
to call tableView.reload data, so that we call those data source methods again and update our table
206

207
00:18:42,440 --> 00:18:48,200
view with the newItem because we've gotten rid of our save method.
207

208
00:18:48,200 --> 00:18:52,580
So, now all that's left to do is to run our app and see if it works.
208

209
00:18:52,580 --> 00:18:57,500
So as you can see, we've got all of the four categories that we added on earlier.
209

210
00:18:57,890 --> 00:19:04,340
And if I go inside Shopping as a category, then you can have a look inside the Realm Browser as well.
210

211
00:19:04,340 --> 00:19:11,480
You can see that Shopping does in fact contain a list of item objects, but it's currently completely
211

212
00:19:11,510 --> 00:19:15,170
empty, i.e., there are no items associated with Shopping.
212

213
00:19:15,170 --> 00:19:22,760
So let's go ahead and add one, let's say, "Buy Apples," hit Add Item, and you can see that, firstly, the item
213

214
00:19:22,760 --> 00:19:27,860
model gets incremented with a single new object.
214

215
00:19:28,040 --> 00:19:35,810
But also inside category, inside that link, you can see that when you click on this item, it takes you
215

216
00:19:36,110 --> 00:19:38,050
to that particular item.
216

217
00:19:38,330 --> 00:19:41,380
And it's now equal to 1.
217

218
00:19:41,390 --> 00:19:42,920
So as we add more,
218

219
00:19:49,550 --> 00:19:57,320
you can see that, firstly, they're getting fetched back in alphabetical order because of that sort that
219

220
00:19:57,320 --> 00:19:59,200
we specified here,
220

221
00:19:59,320 --> 00:20:06,960
and they're also showing up over here, and you can hover over it in order to see it at a glance.
221

222
00:20:07,040 --> 00:20:14,660
So in the next lesson, we're going to look at the next letter in CRUD which is "U" and how you can update
222

223
00:20:14,720 --> 00:20:17,270
your data inside Realm.
223

224
00:20:17,300 --> 00:20:20,270
So for all of that and more, I'll see you on the next lesson.
