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