0
1
00:00:00,300 --> 00:00:08,190
In the last lesson, we saw how we could use the Cloud Firestore to start saving our user's messages to
1

2
00:00:08,220 --> 00:00:10,920
our Cloud-based database.
2

3
00:00:10,920 --> 00:00:17,520
In this lesson, I want to show you how you can read from the database, so that we can populate our app
3

4
00:00:17,640 --> 00:00:24,700
and all the message bubbles. If you take a look at the documentation under the Getting started section
4

5
00:00:24,820 --> 00:00:26,660
of Cloud Firestore,
5

6
00:00:26,680 --> 00:00:33,530
You can see that it has a section on reading data. We can, again, use our database reference.
6

7
00:00:33,530 --> 00:00:38,390
Tap into a collection with a particular name that exists in your database.
7

8
00:00:38,410 --> 00:00:46,210
So in our case, it's messages. And then we can call getDocuments to get a hold of the documents in that
8

9
00:00:46,210 --> 00:00:47,750
particular collection.
9

10
00:00:47,830 --> 00:00:54,070
We've currently only got one document which is this "Test Message" right here. And I want to be able to
10

11
00:00:54,070 --> 00:00:59,530
show that Text Message when we load up our FlashChat app.
11

12
00:00:59,530 --> 00:01:06,820
So I'm going to log out and go back to the home screen. And I want to be able to modify the code, so that
12

13
00:01:06,980 --> 00:01:11,370
inside viewDidLoad, when that ChatViewController is being loaded up,
13

14
00:01:11,800 --> 00:01:20,740
I want to call a method code loadMessages. And this method loadMessages should be able to pull up
14

15
00:01:21,040 --> 00:01:28,870
all of the current data that's inside our database. And then we're going to use it to populate our table
15

16
00:01:28,870 --> 00:01:30,460
view.
16

17
00:01:30,580 --> 00:01:36,580
So the first thing that should happen when we're loading messages from our Cloud Firestore is we should
17

18
00:01:36,580 --> 00:01:39,340
clear these dummy messages.
18

19
00:01:39,340 --> 00:01:47,680
So let's go ahead and set this messages array to an empty array, so that we clear it out. And the next
19

20
00:01:47,680 --> 00:01:50,940
thing we're going to do is we're going to tap into our database.
20

21
00:01:50,950 --> 00:01:54,430
So we're gonna say db.collection
21

22
00:01:54,700 --> 00:02:02,850
and remember the collection path is going to be using that constant K.FStore.collection name.
22

23
00:02:03,100 --> 00:02:09,950
And then we're going to call that method that we saw over here which is getDocuments. So let's call
23

24
00:02:09,950 --> 00:02:17,460
getDocuments and it has a completion block. With this completion block selected,
24

25
00:02:17,540 --> 00:02:21,680
let's hit enter. And you can see that we get two things back
25

26
00:02:21,680 --> 00:02:27,500
once this process of getting documents completes. One is a query snapshot,
26

27
00:02:28,040 --> 00:02:32,710
so we'll just call that querySnapshot, and then the other is an error
27

28
00:02:32,750 --> 00:02:33,890
if there was one.
28

29
00:02:33,920 --> 00:02:36,430
So this is an optional error.
29

30
00:02:36,500 --> 00:02:41,480
It will only be equal to something if that actually were something bad that happened.
30

31
00:02:41,480 --> 00:02:47,090
So in our code here, we're going to check to see if let e =  error,
31

32
00:02:47,240 --> 00:02:50,840
so if error is not nil, then we're going to print.
32

33
00:02:50,960 --> 00:03:00,860
"There was an issue retrieving data from a Firestore." And then we're going to add on that "e" at the end
33

34
00:03:00,860 --> 00:03:02,260
to print it out as well.
34

35
00:03:02,330 --> 00:03:09,770
But if there were no errors, then we want to be able to tap into this query snapshot objects and get
35

36
00:03:09,770 --> 00:03:18,950
hold of the data that's contained inside.This querySnapshot is actually an optional. It's an optional
36

37
00:03:18,950 --> 00:03:20,330
query snapshot.
37

38
00:03:20,330 --> 00:03:26,390
So if we wanted a little bit more detail on what that thing actually is, then instead of going into the
38

39
00:03:26,390 --> 00:03:30,890
Guides section, we go to the Reference section on the Firebase docs.
39

40
00:03:31,100 --> 00:03:39,410
And if we're going to iOS Swift and locate the Firestore section, and then if you go into Classes and
40

41
00:03:39,410 --> 00:03:43,490
scroll down, you should be have to find Query Snapshot right here.
41

42
00:03:43,490 --> 00:03:50,240
So this is an NSObject and it is created from a class called QuerySnapshot
42

43
00:03:50,900 --> 00:03:58,100
and this has a bunch of properties like the query on which you called get documents, or the metadata,
43

44
00:03:58,160 --> 00:04:04,490
or whether if it's empty, or the count of documents inside. But the one that we want is actually this
44

45
00:04:04,490 --> 00:04:14,260
array called documents because this contains the actual documents that saved in that collection. 
45

46
00:04:14,260 --> 00:04:15,010
Coming back over here,
46

47
00:04:15,010 --> 00:04:18,160
let's go ahead and tap into our 
47

48
00:04:18,160 --> 00:04:26,320
querySnapshot.documents. And this, as you can see, is an array of QueryDocumentSnapshot objects.
48

49
00:04:26,440 --> 00:04:33,690
So what if we wanted to see what a QueryDocumentsSnapshot looks like and what properties does it have,
49

50
00:04:33,700 --> 00:04:39,000
well then we can try and find it inside theFirestore classes and it's right here.
50

51
00:04:39,100 --> 00:04:45,790
So it tells us that the document is guaranteed to exist and its data can be extracted with the data
51

52
00:04:45,820 --> 00:04:50,690
property or simply by subscripting the array.
52

53
00:04:50,770 --> 00:04:59,410
That means we can either say square brackets zero to get the first item from the documents array,
53

54
00:04:59,410 --> 00:05:07,480
and then we can tap into the data property which, notice, it's now a dictionary with a key-value pair.
54

55
00:05:08,020 --> 00:05:19,210
So as long as you know the key, say, for example, in our case, we had a K.FStore.senderField,
55

56
00:05:19,240 --> 00:05:22,760
then we should be able to get the value of that piece of data.
56

57
00:05:23,110 --> 00:05:28,830
But our code is now looking quite terse and it's kind of hard to understand what's going on.
57

58
00:05:28,960 --> 00:05:30,870
So let's make this a little bit better.
58

59
00:05:32,170 --> 00:05:35,180
Now, we know that the query snapshot is an optional.
59

60
00:05:35,410 --> 00:05:43,330
So let's first use an "if let" to bind these documents if they exist to a new constant.
60

61
00:05:43,330 --> 00:05:51,520
Let's call this snapshotDocuments and we're going to set it to equal 
61

62
00:05:51,530 --> 00:05:53,600
querySnapshot?.documents.
62

63
00:05:53,600 --> 00:05:59,310
So, now we have this snapshotDocuments which is an array of QueryDocumentSnapshots,
63

64
00:05:59,540 --> 00:06:01,730
and it's now fully unwrapped.
64

65
00:06:01,970 --> 00:06:11,990
So the next step is to loop through this array of documents snapshots and to be able to tap into the
65

66
00:06:11,990 --> 00:06:14,180
data of each of them.
66

67
00:06:14,180 --> 00:06:22,190
We can, for example, create a for loop and say, for doc in snapshotDocuments.
67

68
00:06:22,220 --> 00:06:32,750
So for each of these documents in there, we're going to go ahead and print the doc.data, and this
68

69
00:06:32,750 --> 00:06:35,720
data is now a key-value pair.
69

70
00:06:36,230 --> 00:06:39,680
So let's go ahead and run our code and see what we get printed.
70

71
00:06:44,010 --> 00:06:48,920
So as soon as it loads up, we get our data printed from our documents
71

72
00:06:49,140 --> 00:06:53,940
and it has a body of Test message, a sender: 1@2.com.
72

73
00:06:54,570 --> 00:06:58,400
And if we go ahead and send another message, say,
73

74
00:06:58,440 --> 00:07:01,260
"Testing Testing."
74

75
00:07:01,530 --> 00:07:05,220
Hit send. aAnd it's successfully saved our data.
75

76
00:07:05,250 --> 00:07:10,560
So let's log out and log back in again to trigger that viewDidLoad.
76

77
00:07:10,620 --> 00:07:11,590
You can see this time.
77

78
00:07:11,610 --> 00:07:16,200
We're now getting both messages printed out because of our for loop.
78

79
00:07:16,200 --> 00:07:19,690
So we're looping through all of the documents inside this array,
79

80
00:07:19,800 --> 00:07:26,130
and for each of them, we're tapping into the data object and we're printing it out down here.
80

81
00:07:26,130 --> 00:07:33,750
So instead of printing that data object, what if we could save it as a piece of data?
81

82
00:07:33,870 --> 00:07:43,650
So let data = doc.data, and then we can divide up that data into the sender value and the body value.
82

83
00:07:43,650 --> 00:07:53,880
So we can say let sender = data, and we're going to access the value using the square brackets
83

84
00:07:53,910 --> 00:08:00,630
because it's a dictionary, and we're gonna tap into K.FStore.senderfield.
84

85
00:08:00,630 --> 00:08:05,730
Now, remember that when you're working with dictionaries and when you're trying to retrieve things from
85

86
00:08:05,730 --> 00:08:13,800
dictionaries, because we're using a string to retrieve that piece of data and they can obviously be typos
86

87
00:08:13,800 --> 00:08:21,450
in the string, then the item that we get out or the value is going to be, again, an optional. And even worse,
87

88
00:08:21,450 --> 00:08:22,960
it's not just an optional,
88

89
00:08:23,130 --> 00:08:31,380
but we don't even know what the data type is, because this data has a data type of "Any."
89

90
00:08:31,380 --> 00:08:37,620
And the reason is because Firebase allows us to store lots of different data types into the database,
90

91
00:08:37,670 --> 00:08:44,130
say, we could have had a field called time, right? Like when the message was sent.
91

92
00:08:44,580 --> 00:08:47,100
Well, that obviously wouldn't be a string.
92

93
00:08:47,100 --> 00:08:53,850
So the flexibility on Firebase's part makes us have to do a little bit more work when we're trying
93

94
00:08:53,850 --> 00:08:55,890
to retrieve the items.
94

95
00:08:55,890 --> 00:09:04,620
So we know that the sender data is gonna be a string. Instead of having a optional "Any,"
95

96
00:09:04,620 --> 00:09:11,790
I'm going to use the conditional downcast to cast it into a string.
96

97
00:09:11,790 --> 00:09:16,120
Now, we should have an optional string instead.
97

98
00:09:16,170 --> 00:09:22,890
So as long as we're able to retrieve the senderField from our data, then we should end up with some
98

99
00:09:22,890 --> 00:09:24,630
sort of string.
99

100
00:09:24,660 --> 00:09:33,540
So what we can do is we can use an "if let" to bind this sender to this value right here.
100

101
00:09:33,540 --> 00:09:38,690
And in addition, I'm also going to tag on a constant to save on messageBody,
101

102
00:09:39,330 --> 00:09:49,920
and this is going to be set to equal data, and a set of square brackets, and then K.FStore.bodyField.
102

103
00:09:49,920 --> 00:09:52,210
And then here,
103

104
00:09:52,230 --> 00:09:56,660
this is also going to be a question mark, Any,
104

105
00:09:56,700 --> 00:10:03,450
so we also have to conditionally downcast it to a string because we know that the message is also a
105

106
00:10:03,450 --> 00:10:04,410
string data type.
106

107
00:10:05,160 --> 00:10:13,320
So if both of these pieces succeed, then we should end up with a sender that is now a non-optional string
107

108
00:10:13,890 --> 00:10:18,850
and a messageBody which is also a non-optional string.
108

109
00:10:19,470 --> 00:10:27,300
So we're now ready to create a message object using the Message class because it has two properties
109

110
00:10:27,390 --> 00:10:29,730
that need filling: the sender and body.
110

111
00:10:29,730 --> 00:10:37,380
And we've now got both of them back from Firebase. And once we've type cast our sender and messageBody
111

112
00:10:37,380 --> 00:10:44,730
to strings, then we're going to create a newMessage which is going to be created from the Message class.
112

113
00:10:44,760 --> 00:10:47,020
So this one right here with a capital M.
113

114
00:10:47,250 --> 00:10:52,850
And then we're gonna initialize it with the sender being equal to this sender.
114

115
00:10:52,850 --> 00:10:59,070
And if you want to make it just a little bit more clear which one is which, then it might be easier if
115

116
00:10:59,070 --> 00:11:01,860
you have it as messageSender instead.
116

117
00:11:01,930 --> 00:11:07,050
So we can see that we're creating a newMessage. And for the sender property, we're giving it this value
117

118
00:11:07,170 --> 00:11:13,020
which comes from right here. And then the body will set as the messageBody
118

119
00:11:13,020 --> 00:11:20,080
and now we've got a newMessage ready to add to our array of messages which is currently empty.
119

120
00:11:20,130 --> 00:11:21,270
So let's go ahead and do that.
120

121
00:11:21,270 --> 00:11:27,720
Let's tap into the messages array and append this new message to it,
121

122
00:11:27,750 --> 00:11:37,510
so we should now have a message array with two items. And because we're inside a closure right now,
122

123
00:11:37,510 --> 00:11:46,990
we have to, of course, add the keyword "self" in front of messages. So, now we no longer need these dummy messages
123

124
00:11:47,260 --> 00:11:53,080
because we're going to be getting the live messages that are coming back from Firebase.
124

125
00:11:53,110 --> 00:11:57,450
Now, notice how we're calling load messages inside viewDidLoad,
125

126
00:11:57,790 --> 00:12:06,100
but the time point where we add our message to our messages array actually happens inside our closure
126

127
00:12:06,130 --> 00:12:07,130
right here.
127

128
00:12:07,210 --> 00:12:10,260
So our closure could be triggered at any point
128

129
00:12:10,300 --> 00:12:15,700
and it really depends on your internet connection as to how long it would take for you to get back these
129

130
00:12:15,700 --> 00:12:22,210
documents. Very often, what you'll find is that even though this is being triggered right at the point
130

131
00:12:22,210 --> 00:12:29,650
when view is being loaded up, the message is not added in time for it to be shown inside the table view
131

132
00:12:30,100 --> 00:12:36,240
when it starts tapping into the messages array and populating each of the cells.
132

133
00:12:36,250 --> 00:12:37,450
So when we run our app,
133

134
00:12:40,460 --> 00:12:46,670
you can see that despite everything we've done, we're still not seeing any messages show up here, and
134

135
00:12:46,670 --> 00:12:48,600
it's actually completely empty.
135

136
00:12:49,160 --> 00:12:50,340
So what can we do?
136

137
00:12:50,360 --> 00:12:56,660
How can we trigger our table view to call these methods again and reload the table view?
137

138
00:12:57,500 --> 00:12:59,650
Well, there's a method for that.
138

139
00:12:59,750 --> 00:13:08,810
So once we're done appending our message to messages, we can go ahead and call tableView.reloadData,
139

140
00:13:08,960 --> 00:13:17,630
and what this will do is it will be able to tap into the tableView and trigger those data source
140

141
00:13:17,630 --> 00:13:18,860
methods again.
141

142
00:13:19,130 --> 00:13:28,310
But because we're inside a closure, we have to add the word "self" in front. And remember another good practice
142

143
00:13:28,310 --> 00:13:34,070
thing to do is whenever you're trying to manipulate the user interface, like in this case, when we're
143

144
00:13:34,070 --> 00:13:41,660
trying to update the tableView and you're inside a closure, a good idea is to get a hold of the main queue.
144

145
00:13:42,020 --> 00:13:52,190
So we can say DispatcaQueue.main.async, and then inside this block is where we trigger the update
145

146
00:13:52,250 --> 00:13:53,380
to our tableView.
146

147
00:13:54,140 --> 00:14:01,340
And this just ensures that because this process of fetching documents happens in a closure, that means
147

148
00:14:01,340 --> 00:14:03,620
it's happening in the background.
148

149
00:14:03,620 --> 00:14:11,180
And when we're ready to update our tableView with the new messages, we have to fetch the main thread
149

150
00:14:11,330 --> 00:14:18,560
which is the process that's happening in the foreground. And it's on that thread that we actually update
150

151
00:14:18,620 --> 00:14:20,000
our data.
151

152
00:14:20,000 --> 00:14:26,240
So let's run our app and let's see if it works.
152

153
00:14:26,300 --> 00:14:31,540
So once we've logged in and we've gotten hold of the messages, it took maybe a fraction of a second for
153

154
00:14:31,540 --> 00:14:38,080
it to load up, but we now see our "Text message" and "Testing Testing" show up on screen.
154

155
00:14:38,080 --> 00:14:45,160
And the one last thing that we should fix while we're here is notice how when I type a new message and
155

156
00:14:45,190 --> 00:14:46,780
I click send,
156

157
00:14:46,780 --> 00:14:49,430
nothing actually happens in our table,
157

158
00:14:49,990 --> 00:14:53,370
even though we've successfully saved our data.
158

159
00:14:53,470 --> 00:14:54,970
So what do we need?
159

160
00:14:54,970 --> 00:15:03,460
Well, we need another call to get documents or, alternatively, we can use another functionality that Firebase has
160

161
00:15:03,460 --> 00:15:11,260
which is to listen for changes in our database collection. And that is what we're going to tackle
161

162
00:15:11,440 --> 00:15:13,720
in the next lesson.
162

163
00:15:13,810 --> 00:15:14,470
I'll see you there.
