0 1 00:00:00,210 --> 00:00:07,200 Hey, guys. In this lesson, we're going to be learning how to use Cloud Firestore. Firebase's main product, 1 2 00:00:07,230 --> 00:00:13,160 essentially, and we're going to be using it to store the messages that our users sent. 2 3 00:00:13,170 --> 00:00:20,570 So, first of all, head over to the documentation page Firebase on google.com/docs/guides. 3 4 00:00:20,700 --> 00:00:28,080 And here you're going to navigate to Cloud Firestore and we're gonna go to the Get started section. 4 5 00:00:28,080 --> 00:00:30,930 So feel free to have a read around if you have a moment. 5 6 00:00:31,020 --> 00:00:36,240 But, essentially, we're going to follow this guide step by step like what we did with the authentication 6 7 00:00:36,240 --> 00:00:37,410 module. 7 8 00:00:37,410 --> 00:00:42,270 So the first thing we have to do is to create a new database. 8 9 00:00:42,690 --> 00:00:48,130 Let's head over to our console and select our Firebase app. 9 10 00:00:48,300 --> 00:00:55,620 So mine was called Flash Chat iOS13. And now we land on our console page for this project. 10 11 00:00:55,620 --> 00:00:58,950 So we're going to select the database tab over here on the left. 11 12 00:00:59,220 --> 00:01:04,200 And notice how Firebase actually has two different databases. 12 13 00:01:04,200 --> 00:01:11,460 One is the brand spanking new Cloud Firestore which has now been officially released, and the other 13 14 00:01:11,550 --> 00:01:15,990 is the real time database which was their original product. 14 15 00:01:15,990 --> 00:01:21,330 Now, there's actually a section on "Find out if Cloud Firestore is right for you." 15 16 00:01:21,330 --> 00:01:27,870 So choosing between these two databases. But, basically, the overview of it is that realtime database 16 17 00:01:27,870 --> 00:01:34,270 was their initial product. And it was just a way of being able to save JSONS in the Cloud. 17 18 00:01:34,410 --> 00:01:41,100 But as more and more people started using Firebase, they wanted more features, and especially things like 18 19 00:01:41,100 --> 00:01:47,810 querying and ordering, and the kind of things that you get from modern databases like MongoDB. 19 20 00:01:47,820 --> 00:01:50,460 So Cloud Firestore is the solution, 20 21 00:01:50,580 --> 00:01:55,570 and that is what we're going to be learning how to use in this module. 21 22 00:01:55,590 --> 00:02:02,100 Now, in terms of the dynamics at Firebase, even though they are still supporting realtime database, 22 23 00:02:02,100 --> 00:02:05,970 you can see that they're really trying to hide it by putting it all the way down here. 23 24 00:02:06,450 --> 00:02:11,790 And I have a sneaking suspicion that because it's Google, you never know what they do with the older 24 25 00:02:11,790 --> 00:02:17,630 products. And if there isn't enough demand, then I don't know how long they'll still maintain that 25 26 00:02:17,630 --> 00:02:18,480 realtime database. 26 27 00:02:19,020 --> 00:02:21,540 So let's learn the latest and greatest. 27 28 00:02:21,630 --> 00:02:27,370 Go ahead and click on this button, create database under Cloud Filestore. 28 29 00:02:27,680 --> 00:02:32,380 Now, we get to set the rules for how to access our Cloud Firestore. 29 30 00:02:32,450 --> 00:02:39,800 We can either start in production mode which will not allow any third party, so any of the apps or any 30 31 00:02:39,800 --> 00:02:46,220 users to read and write, which is going to make it very hard to test our app, or we can start in test 31 32 00:02:46,220 --> 00:02:52,270 mode which allows anyone with a reference to our database to be able to read or write to it for 30 days. 32 33 00:02:52,340 --> 00:02:53,490 And this is perfect 33 34 00:02:53,510 --> 00:02:59,630 while we're getting up and running and testing our app. And we're going to be using the test mode until 34 35 00:02:59,630 --> 00:03:05,270 we're ready to add some rules around who's allowed to read and write from our database which we'll do 35 36 00:03:05,270 --> 00:03:06,870 at the very end. 36 37 00:03:06,920 --> 00:03:11,100 So let's go ahead and select test mode, and then hit next. 37 38 00:03:11,510 --> 00:03:18,560 And now we get to choose a Cloud Firestore location. Now, the location that you choose 38 39 00:03:18,620 --> 00:03:25,670 doesn't really matter that much, unless you have a lot of users, because, obviously, the closer you are 39 40 00:03:25,700 --> 00:03:33,160 in terms of your region to your end user, then the faster will be to retrieve things from the database. 40 41 00:03:33,320 --> 00:03:36,950 But note that there's also differences in cost. 41 42 00:03:36,950 --> 00:03:40,840 So, for example, certain areas will cost more than other areas. 42 43 00:03:40,940 --> 00:03:43,640 I recommend going with just us-central, 43 44 00:03:43,640 --> 00:03:50,360 and this is the default selection, and then go ahead and click Done. At this point, with our small amounts 44 45 00:03:50,360 --> 00:03:50,870 of data, 45 46 00:03:50,900 --> 00:03:54,940 you really won't notice a difference in speed. 46 47 00:03:55,310 --> 00:03:59,000 Now, it's going to take a little while to create your Firestore. 47 48 00:03:59,300 --> 00:04:07,070 But once this loading screen is done, it will take you to your brand-new database. 48 49 00:04:07,070 --> 00:04:14,390 So now we're ready to start creating some documents in our database. Heading back to the docs, 49 50 00:04:14,390 --> 00:04:19,600 we've already completed this step creating the Firestore database on Firebase. 50 51 00:04:20,390 --> 00:04:23,860 And we've also already set up our development environment. 51 52 00:04:24,260 --> 00:04:32,550 So when we install the Firebase Authentication Module, we also added the Firebase Firestore pod. 52 53 00:04:32,570 --> 00:04:39,410 So if we take a look in our project and you take a look down here where you've got pods and pod file, 53 54 00:04:39,740 --> 00:04:45,600 you can see we've already got Firebase/Auth and Firebase Firestore. 54 55 00:04:45,610 --> 00:04:52,390 Now, notice how here, they've also added Firebase Core which is the core module that these Firestore modules 55 56 00:04:52,390 --> 00:04:55,200 and authentication modules all depend on. 56 57 00:04:55,330 --> 00:05:01,750 But since a few months ago, it no longer is required for you to explicitly add the pod into your pod 57 58 00:05:01,750 --> 00:05:02,110 file. 58 59 00:05:02,410 --> 00:05:04,360 So we're actually good to go. 59 60 00:05:04,360 --> 00:05:09,100 And this optional part if we're using Swift extensions, we actually don't need either. 60 61 00:05:09,100 --> 00:05:16,000 So the only thing you need is this Firebase firestore. And in order to check to make sure that that's 61 62 00:05:16,000 --> 00:05:17,220 already been added, 62 63 00:05:17,290 --> 00:05:23,670 if you go down here and you expand the Pods folder, you should be at to see a module called Firebase 63 64 00:05:23,760 --> 00:05:24,680 Firestore. 64 65 00:05:24,910 --> 00:05:28,650 If you see that, then you're good to go. 65 66 00:05:29,030 --> 00:05:33,690 So the next step is to initialize our Cloud of Firestore. 66 67 00:05:33,710 --> 00:05:36,040 So in our AppDelegate.swift, 67 68 00:05:36,120 --> 00:05:41,810 we've already imported Firebase and we've already configured the Firebase app in order to use Firebase 68 69 00:05:41,810 --> 00:05:42,970 Authentication. 69 70 00:05:43,220 --> 00:05:48,910 But now we're also going to use the Firestore module to create a new database. 70 71 00:05:49,010 --> 00:05:55,760 So let's copy this line of code and let's go into our AppDelegate.swift, and just below where we've 71 72 00:05:55,760 --> 00:05:57,940 configured our Firebase app, 72 73 00:05:58,040 --> 00:06:06,430 I'm going to paste that line right here. So this line of code creates our Firebase Firestore. And to 73 74 00:06:06,430 --> 00:06:11,280 test to make sure that it actually is working and creating that Firestore, 74 75 00:06:11,290 --> 00:06:14,610 we're simply going to go ahead and print the db. 75 76 00:06:14,860 --> 00:06:21,670 So let's go ahead and run our app so that the AppDelegate gets triggered and when our app has finished 76 77 00:06:21,730 --> 00:06:22,800 launching. 77 78 00:06:22,870 --> 00:06:28,930 So when we see our welcome screen, then that means this block of code has been triggered. And, indeed, down 78 79 00:06:28,930 --> 00:06:29,190 here, 79 80 00:06:29,200 --> 00:06:32,470 I see my Firestore reference being printed. 80 81 00:06:32,470 --> 00:06:37,990 So that means that this line of code is working and we're now ready to move on to the next step. 81 82 00:06:38,590 --> 00:06:46,780 Now, if you don't see this, the first thing I recommend you check is that your pod --version 82 83 00:06:46,840 --> 00:06:50,060 is 1.8.4 or above. 83 84 00:06:50,080 --> 00:06:57,010 I've noticed that if I haven't updated my CocoaPods and I'm using a lower version, when I tried to install 84 85 00:06:57,040 --> 00:07:04,210 Firebase Firestore, this line of code doesn't actually work and I don't get anything printed, or I end 85 86 00:07:04,210 --> 00:07:05,840 up with a crash. 86 87 00:07:05,950 --> 00:07:08,270 So check that your version is higher than this 87 88 00:07:08,320 --> 00:07:16,720 and if it's not, then be sure to run sudo gem install CocoaPods, so that you install the latest version 88 89 00:07:16,810 --> 00:07:24,230 of CocoaPods and it gets updated. But if like me, you're getting that reference printed, then it means 89 90 00:07:24,230 --> 00:07:31,760 that we're ready to go ahead and put in some data. Heading over to the ChatViewController, we're going 90 91 00:07:31,760 --> 00:07:38,840 gonna go ahead and create our first piece of data based on what the user has typed in. And the time point 91 92 00:07:38,840 --> 00:07:44,900 where we actually send our data to our Firestore should be the moment that the user presses the send 92 93 00:07:44,900 --> 00:07:45,540 button, 93 94 00:07:45,560 --> 00:07:52,840 so inside this IBAction. The first thing that I'm going to do is I'm going to create a Constant that 94 95 00:07:52,840 --> 00:07:55,240 stores the message body. 95 96 00:07:55,240 --> 00:08:01,600 So this is going to be equal to whatever is inside our messageTextField at the moment when the user 96 97 00:08:01,600 --> 00:08:07,450 presses the send button. Because if you think about it, once they've typed a message and they're done, 97 98 00:08:07,780 --> 00:08:13,090 then they're going to press on this little button and it's in that moment in time where we grab hold 98 99 00:08:13,090 --> 00:08:19,220 of the text inside this text field, and we set it as the message body ready to be sent to our Firestore. 99 100 00:08:19,690 --> 00:08:24,040 So I'm gonna set the message body to messageTextField.text. 100 101 00:08:24,040 --> 00:08:31,840 And next, I want to also create the sender part of my message. Because remember, if you take a look inside 101 102 00:08:31,870 --> 00:08:37,890 our message model, we've got two properties: a sender and a body, which we need to fill. 102 103 00:08:37,900 --> 00:08:41,800 So we've already got the body. But how do we get hold of the sender? 103 104 00:08:41,800 --> 00:08:44,320 How can we get hold of the logged in users' 104 105 00:08:44,350 --> 00:08:46,090 email address? 105 106 00:08:46,480 --> 00:08:53,560 Well, if we take a look back at the documentation for Authentication and under iOS, if we take a look 106 107 00:08:53,560 --> 00:09:01,150 at the Manage Users section, you can see that you get hold of the currently signed-in user by tapping 107 108 00:09:01,150 --> 00:09:03,610 into the current user property. 108 109 00:09:03,790 --> 00:09:10,600 And this happens to be a optional because if there's no user signed in, then current user is going to 109 110 00:09:10,600 --> 00:09:12,310 be equal to nil. 110 111 00:09:12,310 --> 00:09:17,560 And in Swift, things are only allowed to be equal to nil when they're an optional. 111 112 00:09:17,620 --> 00:09:24,070 So heading back to our project, we're going to create a constant code messageSender and we gonna set 112 113 00:09:24,070 --> 00:09:35,730 it to equal Auth.auth().currentUser. So Auth.auth().currentUser 113 114 00:09:35,810 --> 00:09:38,060 and this is a user question mark. 114 115 00:09:38,630 --> 00:09:40,940 So we don't just want the user though, 115 116 00:09:41,030 --> 00:09:42,710 we can't do anything with that. 116 117 00:09:42,740 --> 00:09:48,410 What we actually want is to drill even one level deeper and we're going to write .email. 117 118 00:09:48,440 --> 00:09:55,130 So if we have a current user, then we're going to get a hold of their email and save it inside 118 119 00:09:55,190 --> 00:09:57,140 our messageSender property. 119 120 00:09:57,140 --> 00:09:59,620 Now notice how this is a optional string. 120 121 00:09:59,660 --> 00:10:03,890 And this is also an optional string because the text field could be empty, 121 122 00:10:03,890 --> 00:10:05,760 that might not be any text. 122 123 00:10:05,900 --> 00:10:14,210 So it makes perfect sense to use an "if let" to actually bind both of these options to a constant as 123 124 00:10:14,210 --> 00:10:17,540 long as they are not nil. 124 125 00:10:17,540 --> 00:10:24,890 Now, we've said if the messageTextfield.text is not nil, then save it inside message body. If there 125 126 00:10:24,890 --> 00:10:30,330 is a current user logged in, then save their email inside the messageSender. 126 127 00:10:30,380 --> 00:10:38,540 And now if these two pieces of information are not nil, then we go into this block of code where we can 127 128 00:10:38,630 --> 00:10:47,130 send these pieces of data to our Firebase Firestore. The first thing that I'm going to do here is I'm going 128 129 00:10:47,130 --> 00:10:50,850 to create a reference to our database at the very top. 129 130 00:10:51,330 --> 00:10:58,580 So I'm going to call this again db and I'm gonna set it to equal Firestore.firestore. 130 131 00:10:58,710 --> 00:11:04,630 Now, once I've created that reference, then I can use it down here. 131 132 00:11:04,660 --> 00:11:05,950 So how do we use it? 132 133 00:11:05,950 --> 00:11:12,040 Well, if we take a look at the getting started section for our Cloud Firestore, you can see that you 133 134 00:11:12,040 --> 00:11:21,400 can add pieces of data by tapping into a collection, and then using the addDocument method to send a 134 135 00:11:21,400 --> 00:11:23,530 dictionary over. 135 136 00:11:23,530 --> 00:11:30,570 So let's try this out. Let's tap into our database, and then tap into a collection. 136 137 00:11:30,570 --> 00:11:34,660 Now, this collection expects a name as a string. 137 138 00:11:34,680 --> 00:11:39,870 Now, in our case, we're probably going to call it messages. But just to make sure that we don't make any 138 139 00:11:39,870 --> 00:11:43,340 typos in the future when we access this collection. 139 140 00:11:43,350 --> 00:11:47,340 So when we're trying to retrieve from it, we're going to use our constants. 140 141 00:11:47,880 --> 00:11:50,620 So we're going to use our collectionName here. 141 142 00:11:51,270 --> 00:11:59,880 And notice how in this case, this constant collection name is inside a struct called FStore. But the 142 143 00:12:00,030 --> 00:12:06,870 FStore struct is itself nested inside the K constant struct. 143 144 00:12:06,870 --> 00:12:12,260 So in order to access this piece of information, we have to drill two levels deep. 144 145 00:12:12,270 --> 00:12:25,730 So we'll write K.FStore.collectionName. And while you could put all of these constants simply 145 146 00:12:25,880 --> 00:12:34,430 at the top level inside the K struct, it makes it easier this way that you can organize them into different 146 147 00:12:34,430 --> 00:12:35,190 groups. 147 148 00:12:35,330 --> 00:12:41,900 And when you call them in the code, it makes a little bit more sense. Say, the collectionName is associated 148 149 00:12:41,900 --> 00:12:49,100 with the Firebase Firestore from the constants file. At least, I think this is more readable and slightly 149 150 00:12:49,100 --> 00:12:58,330 better code. So now that we've tapped in to this collection which is code messages, let's go ahead and 150 151 00:12:58,630 --> 00:13:05,710 call that method addDocument, and we're going to call the one which has a completion handler, so that 151 152 00:13:05,710 --> 00:13:08,370 we'll hear if there are any errors. 152 153 00:13:08,560 --> 00:13:16,120 Let's go ahead and hit enter to insert this bit of code. And the data that I want to send is, of course, 153 154 00:13:16,120 --> 00:13:18,420 is going to be a dictionary. 154 155 00:13:18,550 --> 00:13:21,010 And so it's going to have a key value pair. 155 156 00:13:21,310 --> 00:13:26,620 And notice if I just back up a little bit that the key has to be of type String, 156 157 00:13:26,620 --> 00:13:29,710 nut the values can be Any data type, 157 158 00:13:29,800 --> 00:13:32,990 so it can be an object from a structure or a class. 158 159 00:13:33,100 --> 00:13:35,560 And, essentially, it doesn't matter what it is. 159 160 00:13:35,720 --> 00:13:37,140 Firebase doesn't actually care. 160 161 00:13:37,630 --> 00:13:47,350 So let's go ahead and add our data. And the first piece of data is going to have a key using 161 162 00:13:47,460 --> 00:13:56,830 the K.Ftstore.senderField, and then after a colon, we're going to add the value. 162 163 00:13:56,860 --> 00:14:01,640 So in this case, the value is going to be that messageSender here. 163 164 00:14:02,080 --> 00:14:08,530 So we'll call it messageSender, and then we're going to add a comma to add the next key which is going 164 165 00:14:08,530 --> 00:14:18,030 to be K.FStore.bodyField, and this is going to be equal to the messageBody that we created 165 166 00:14:18,120 --> 00:14:21,900 up here from what the user typed in the messageTextField. 166 167 00:14:23,820 --> 00:14:29,990 Now that we've created our data dictionary, we're ready to create the completion handler. 167 168 00:14:30,240 --> 00:14:37,230 So selecting this placeholder, go ahead and hit enter, and give the error a name of error. 168 169 00:14:37,410 --> 00:14:43,590 And inside this completion block, we're going to check to see if let e = error. 169 170 00:14:43,680 --> 00:14:49,110 So remember, this isn't optional, we might get no error at all. When this case, we're going to print out 170 171 00:14:49,200 --> 00:14:50,940 the error. 171 172 00:14:50,970 --> 00:15:00,940 I'm going to say, "There was an issue saving data to Firestore," and then we're going to just add that 172 173 00:15:01,130 --> 00:15:03,830 "e" right at the end, so that it gets printed out 173 174 00:15:03,940 --> 00:15:07,780 if it's not nil. But we're going to have an "else" statement as well. 174 175 00:15:07,780 --> 00:15:17,100 So if there were no errors when this case, let's print "Successfully saved data." Let's go ahead and hit 175 176 00:15:17,100 --> 00:15:22,020 save and run our app and test it out. 176 177 00:15:22,140 --> 00:15:28,830 So I'm going to log in as 1@2.com. And here, I'm going to type a message. 177 178 00:15:28,830 --> 00:15:35,790 Now, notice that if you have the keyboard enabled, it completely obscures that text field and the send 178 179 00:15:35,790 --> 00:15:36,560 button. 179 180 00:15:36,630 --> 00:15:42,210 We're going to sort this out in a little moment and we're going to make this text field go up to the 180 181 00:15:42,210 --> 00:15:46,390 top and animate alongside the keyboard. 181 182 00:15:46,440 --> 00:15:54,600 But for now, what I want you to do is to either go to Hardware Keyboard and Toggle Software Keyboard, 182 183 00:15:54,900 --> 00:16:01,140 so you can make it go down essentially, or you can use the shortcut command K to do the same thing. 183 184 00:16:01,620 --> 00:16:05,070 Basically, we want to be able to see what we're typing here. 184 185 00:16:05,070 --> 00:16:12,530 So I'm going to write a test message and I'm going to click the send button to trigger our code here. 185 186 00:16:12,690 --> 00:16:19,140 And notice how we've already got that callback completed and we've got successfully saved data printed, 186 187 00:16:19,170 --> 00:16:20,910 so that means there were no errors, 187 188 00:16:20,910 --> 00:16:30,330 and, hopefully, if we go into our Flash Chat iOS 13 database, and we hit refresh on this page, we should 188 189 00:16:30,390 --> 00:16:38,190 be able to see our data being added in here under a collection code messages. And, indeed, there it is. 189 190 00:16:38,190 --> 00:16:44,310 So we've got the body, which is text message, and we've got a sender, which is 1@2.com, and then 190 191 00:16:44,310 --> 00:16:50,880 we've got this I.D. for this piece of data which is automatically created whenever we add a new document 191 192 00:16:51,030 --> 00:16:55,130 to the Firebase Firestore. That's it. 192 193 00:16:55,180 --> 00:16:55,780 There it is. 193 194 00:16:55,780 --> 00:17:03,030 We've managed to save our very first piece of data to our Cloud-based database using Firebase Firestore. 194 195 00:17:03,700 --> 00:17:04,890 In the next lesson, 195 196 00:17:04,900 --> 00:17:12,160 I'm going to show you how we can retrieve data and read from our Firebase Firestore in order to populate 196 197 00:17:12,220 --> 00:17:15,070 the cells of our Flash Chat app. 197 198 00:17:15,130 --> 00:17:17,650 So for all of that and more, I'll see you there.