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.