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
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
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) ♪