So I know for a lot
of you developers out there, the idea of a database
automatically sending you synchronized updated values
in near realtime is like weird and strange. Like when a lot of developers
first started working with a Firebase Realtime Database,
they kind of pined for the good ol' days when you could just make
a simple GET request and kind of skip that whole realtime thing. Turns out though that was hard to do
in Realtime Database land. There was a solution,
but it was kind of clunky, and we generally
steered people away from it. Luckily, with Cloud Firestore,
we've redesigned the SDK from the ground up
so that one-time document fetches and queries are a lot easier. They're more like first-class citizens
in the Cloud Firestore API, and you can go ahead
and use them with impunity. Now that we've gone through the trouble of making the one-time
fetching of data easier, I'm here to tell you,
eh, maybe don't do it. ♪ (rock music) ♪ So the nearly realtime
data synchronization capabilities of Cloud Firestore,
where data just automagically updates from the Cloud is pretty awesome. I would generally encourage anybody
who's using Cloud Firestore to look at adding this kind
of behavior to their app. But when I've talked to developers,
a lot of them are hesitant to go down this realtime route
and it's generally for two reasons. One, realtiminess is weird and strange
and how am I supposed to organize my app
around this kind of behavior? And two, general concerns
around battery usage and/or price. So let's see if we can address
some of these. I think the best way
to get over your fear of the unknown here is to give you an overview
of how these realtime capabilities work and then go over how you might
want to use them in your app. Now when you ask to listen
to a single document or a bunch of documents
at once for a query, that sets a number of things in motion. On the client,
you're going to create a listener. This is essentially a callback
that the SDK fires when there's new data
available from your database. And over on the Cloud,
you're going to be kicking off a process on the database itself
that monitors this specific query to see if there's any new data available. Now the first time you ask
to listen to a document or a query, you're going to get back
all the data you request. I mean it's basically all new data
if you think about it. So if you ask for one specific document,
you're going to get back the contents of that document. If you ask for the top 20
Japanese restaurants in San Francisco, you're going to get back
those 20 restaurants. But from that point on,
your client will only get notified when there's a change
in those 20 documents, whether that's a change
in the document itself, a restaurant gets added or removed or they change their order in that query. Now usually for the sake of protecting your users' data plan and battery, only the documents
that have changed will be sent over. So if Ray's Ramen suddenly changes
their name to Gary's Gyoza, what I'll get back under the hood
is data that looks like this, one document representing
the restaurant that's changed. But over on the client,
the Cloud Firestore library will combine this updated document
with the rest of the cache data, and in the listener,
it will still present me with the entire set of 20 documents. And I know that might
seem weird at first-- why is it sending me
all these documents again?-- but this is usually what you want
as a developer. If you think about it,
the code in your listener is already set up to read in and display
a full set of 20 documents because that's what happens
the first time it's run. So when we get back a full set
of 20 documents for all future changes I can probably reuse this code
over and over again and not have to worry
about special-casing it depending on whether
this is my initial data grab or not. By the way, I think this is one reason
developers might freak out about pricing at first. They see this list
of 20 documents showing up again and again in their listener
and they think that Cloud Firestore is charging them for 20 reads
every time there's an update, but that's not the case. Yes, you get back all 20 documents
in these callbacks but you're usually only getting charged for the one document that is changed. Now if you really do care to know
exactly what's changed in your callback listener,
you can do that. Every snapshot contains
a document changes object, which will give you a list
of every document that's been added, removed,
changed or appears in a different order. I think most often
developers use this information if they want to provide a visual cue
that something has changed. For instance, we could have
an item turn red and fade out if we know that it's been deleted
or add in some gold sparklies when we know that it's been added. Or maybe you're performing
some very expensive processing on your data. In that case,
maybe it also makes sense to only do this on the documents
that have changed. It's kind of up to you. I know we talked about this
in the last episode, but remember
that when you perform a write, those writes are essentially
applied immediately on your local copy of the data. Now when that happens,
any listeners on your client are also notified. Those listeners are fired off
using the updated local client data as your snapshot, so then you can act
on those changes right away. This is all happening
while your data is traveling to the database in the Cloud
in the background. So, several milliseconds later
when your database comes back with the updated data,
Cloud Firestore is smart enough to realize that this updated data matches
what it has locally, and by default, it will quietly ignore
this update from the Cloud. So what does this all mean
if you want to construct an app that behaves nicely
in the land of Realtime updates? First, your listener
is going to be the place where you primarily read in
your Cloud Firestore data and do stuff with it. It will be the place where you convert
your database documents into custom local objects. You'll probably populate
whatever local model you're using, and then you can tell your table view
or labels or what have you to refresh themselves
based on this local model. But then whenever you're looking
to make changes, you're going to want to make
those writes directly to the database documents themselves. That's right. You don't want
to change your local model. You want to make the change
to the original database document. Now that will kick off
the change locally right away, and your listener can once again
update its local model based on the data you're getting back
from the database. Now most cases,
this process is probably simple enough that you'll just kind of blow away
your old model and reconstruct it from scratch
with a new set of data. But you can always get fancier if you want by looking at just the objects
that have changed. But I think the main takeaway here
is that we're seeing the database as the source of truth
and the place we want to make any kind of write operations
with our data. Now let's talk performance, both when it comes to data usage
and battery drain, because I think some developers assume
that keeping open a realtime connection is as much of a battery killer
as streaming down a video, and that is not the case. So yes, anytime there is a listener present,
your user's device will have an open socket connection to the database and while that has a greater
than zero impact on your user's battery, it's probably not going
to be the biggest factor. That's going to be much more influenced by the amount of actual data
you're downloading or having the screen on
on the phone. So don't think that if you shut down
the realtime connection and have your app perform a bunch
of manual one-time fetches instead, you're going to save any battery. In fact, if you keep turning your radio
on and off to make these fetches, there's a good chance it might even
be worse that way, For that matter, having
several active listeners at once isn't by itself going to be
a big drain on resources either. What is going to be a bigger drain
on your user's battery and their data plan
is the downloading of bytes. To oversimplify things a little bit,
the more bytes you download, the more it'll hit your user's battery. So what does that mean for you? Well, if you're concerned
about battery or data usage and you should be,
follow these two rules. First don't download more data
than you need. I know you can put a lot of data
into a single document but if you find that you end up
downloading a lot of data that you almost never use,
stick that extra data into a sub collection
and then only download those extra documents when necessary. Second, make sure you deactivate
your listeners when they're not needed. Generally speaking,
when you add a realtime listener in your <i>ViewController</i>
or your <i>Activity</i>s life cycle, you want to remove it
at the complementary place. Meaning that if you create a listener in your viewWillAppear handler on iOS, deactivate it in your viewWillDisappear. If you create a listener
in your onStart on Android, deactivate it in your onStop
or attach it to the <i>Activity</i> which will automatically unregister it. In a PWA, if you're adding a listener
when a component is created, make sure you deactivate it
when the component is de-initialized. And yes this does mean
that when your user comes back to a screen
they might have visited earlier, it might take a moment
to refresh your latest data because that listener hasn't
been running in the background. But it also means that
if the same document were to change several times in the back end while your user is
somewhere else in your app, their device won't be going
through the trouble of downloading all that intermediate data
that they don't really need, and you won't get billed for those reads. So when would you want
to use Realtime listeners versus a single fetch? Well, I'll be honest. I'd say that, in general, realtime should probably
be your default choice. For starters,
and I know this is counterintuitive, using realtime data makes your app
perform better in areas of spotty network coverage by default. Remember the nice thing
about these realtime listeners is that they're designed
to be run multiple times. So we can do things
like give you the cache data right away then give you updated data
a few hundred milliseconds later when the network actually follows through. Fetches don't work that way. These one-time fetch callbacks are designed to only be called once, so you only have one chance
to fetch that data, which means if your network is slow, we have to wait those three seconds
until we can get back that data or wait several more seconds
until that connection times out before we can serve the cache data, and, you know,
that's not a great experience. There are ways
to improve this, of course. You can manually run two one-time fetches-- one that's explicitly set to fetch
from the cache, and then another that's explicitly set
to fetch from the server-- but, again, this is work
you wouldn't need to do if you just used a realtime listener. Plus, having realtime listeners means that when your client makes changes
to a document, you don't have to add that extra step of manually refetching the data
that's just changed. Again, it will happen automagically
when your listener gets reactivated with sort of this changed cache data. And, of course,
there's the whole realtimey thing which can be a pretty magical experience. If you're powering a turn-based game
or something, it's pretty great to have that data update right away when the other player makes their move. If you didn't have this happen,
you'd need to ask your player to pull to refresh a bunch of times or send a Cloud data message
to your device to tell it to perform an update, and if you're doing all that work anyway, why not just simplify things
and let Cloud Firestore handle it for you. So maybe the better question is
in what situations would it make sense to not go realtime
and use one-time fetch calls? And I think the answer's probably
less a technical one and more of a user experience one. Are there situations in your app where it would be, say, distracting
for new data to arrive? Think about my restaurant review app. Would it be weird if new reviews
popped in or headlines changed while my user was perusing
the reviews for a particular restaurant? It might. Of course, on the other hand it might not. Honestly, this is something
I'd want to look at on a case-by-case basis. Also we talked about this
in an earlier video, but sometimes pagination
can be a little weird if you're trying to combine
paginated data with realtime listeners for one of those infinite
scrolling table view dealies. In these cases, it might sometimes be
better to go with some one-time calls. Finally, there will probably be situations where data is changing more often than your user will care about it. Think about a popular news site
like Reddit, where you've got stories that are constantly getting upvoted
and downvoted. Hey look at that!
Your post on Leroy Jenkins got two votes. Oh, and it's gone negative. I'm sorry. Now admittedly,
I think it'd be kind of neat to see those votes change in realtime and maybe see them switch places, but that constant stream of data
might be kind of expensive in terms of battery and data usage
all to deliver an experience that, while kind of neat,
my user might not care about that much. So in these situations, a one-time fetch
might make more sense. Finally, when it comes to things
like Cloud functions, you're generally looking at calls
that perform a discrete action and then terminate. So there's not much of a point in setting up my realtime listeners there. Same goes for probably 90 percent
of your use cases involving the server SDKs. But remember the main takeaway here is that you should start
thinking of realtime as your default behavior, then only switch to a one-time fetch call when you have a good reason
to make that change rather than vice versa. So there you have it. Hopefully, now this world
of near realtimey experiences isn't quite so weird and strange
and frightening and you can start adding more
of these magical moments into your app. Thanks so much for watching,
and I will see you soon on another episode
of "Get to Know Cloud Firestore." ♪ (music) ♪