Hey there, Android developers.
Interested in getting started with Cloud Firestore,
the autosyncing, autoscaling database in the cloud?
Well, have I got a video for you.
It's this one.
So come on, let's save our data to the cloud
in this episode of Firecasts.
♪ [music] ♪
I'm working on a little app, here, that lets me store an inspirational quote.
I mean, I can't just be expected to start doing work, right?
I need an inspiring quote, preferably from Einstein
or maybe Marilyn Monroe, to motivate me.
And what better way to do this than with an app?
My app here consists of an
and then a second one here where I can store the author's name.
I then have a Save button where we'll save
whatever inspiring message our user has entered into the cloud.
So later, we'll load whatever data our user's saved to the cloud
and display it with a big TextView or something.
So if you want to follow along,
go ahead and create your own version of this app.
Just make sure you have some properties for the
and an
Now, I've already gone ahead and created a Firebase project
in the Firebase Console
and configured the Google Services plug-in
with my JSON file.
If you don't know what I'm talking about,
check out this Getting Started video, here, and then come back.
It's OK., I'll wait.
Now, before we go ahead and write some data,
let's take a moment to understand how Cloud Firestore generally works.
Frankly, this could be the subject of its own separate video,
and it probably will be one day,
but here is the executive summary.
Cloud Firestore is a
That means it stores your data in a big, tree-like structure,
kind of like the original real-time database,
but everything is placed into
You can think of a
It's got key value pairs,
which the Firestore folks like to refer to as
And the values of these things can be any number of things,
from strings to numbers to binary values
to little smaller maps that are kind of JSONy-looking objects.
And
So there are a few rules around the use of these things.
The first is that
No
Second is that
but they can point to
So it's very common to see a
a bunch of
that contain other
In a workout app, for instance, I might have a "Users"
and this
And then each of those
that, in turn, contains a
that this user has performed.
And then maybe each one of these has a "History"
that keeps track of every time the user performed one of these workouts,
maybe along with some average heart rate data
or some other stuff like that.
Now, if you're coming from real-time database-land,
this kind of deep nesting might be giving you heart palpitations.
But don't worry, this kind of data structure is completely normal
in the Cloud Firestore world where queries are shallow,
meaning that when you grab data from a
you'll grab just that
contained in any of the
The third rule is that the root of your database
can only consist of
Now, in a normal production app, this is going to feel pretty natural.
You're going to have your
and your
or what have you.
The one time it's going to seem kind of weird,
it's when you're creating a little test app like ours
and you just want to save like two strings.
So looking at our app, at the top level, I'm going to start with a
that I'm calling "sampleData".
This will then contain one single
called "inspiration".
This
one called quote and another called author.
Incidentally, note that this is basically a global variable that I'm altering--
meaning that I'm not storing one quote per user,
I'm letting everybody in the world alter this one
If I were looking to save a different quote per user,
which I might do in an actual app, I'd probably be setting up
a "users"
Make sense? Alright, let's start building.
So first things first, I need to make sure
I have Firestore installed, and so I'll go into my app's Gradle file
and add this line here to add Firestore to my project.
Obviously, this version number will be different than what's current.
Heck, I'm not even sure this version will work
by the time you see this video.
So do make sure that you grab the most recent version
from our documentation.
And then I'll tell Android Studio to go ahead and sync, and we're good.
So next, in my main activity here, I'm going to head on down
to my
So in there, I will first grab the text from my two
So we'll get the
And then maybe I'll do a check to make sure they're not empty.
Next, I'll create the data I want to save in my document.
Now, this will be a
that I will implement as a
So I'll use the key "quote" and then the string
of my quote
And then I'm going to do the same thing for the author.
And let's do the right thing and Command+Option+C these things
so we can make these key names constants.
OK, that's better.
Now, I just need to save this to the cloud.
And to do that, I need to specify in which
I want to put this data.
So we do that through an object known as a
It turns out I'll be using the same reference
multiple times throughout this project, so I'm going to make it a field
up here in my class.
And let's initialize it up here, too.
Now, there are a few ways of setting my
First, I could call
.collection("sampleData") .document("Inspiration").
Now, I kind of like doing this because it serves as a nice reminder
that you'll always alternate between
but it does start to look a little nutty when you get several layers deep.
This is kind of silly.
So an alternative that I prefer is calling
a slash-separated string as our path.
So in our case, the path would be "sampleData/inspiration".
And just remember that in your path, you're always going to be alternating
between
So now that I've specified my
where I'm going to call
And that'll take in the data I created above
as the contents of my
Now, this call will replace my current
and create it if it doesn't.
It also conveniently creates the "sampleData"
so I don't need to worry about that not existing.
Now I'm going to chain on an
where we can print a debug message.
And let's Command+Option+C this puppy, too.
And then we can add on and
where we can print out any error messages.
And that's it.
For those of you who are all about fewer
you could also handle this with a single
and then just check in here to see if your task is successful.
Either way works.
And honestly, it's just really up to you which method you prefer.
Alright. Let's give this a try.
So I'm going to start up my app,
and I'm going to enter this very inspirational quote
here in the text field.
And I'm pretty sure that was said by Plato.
I'll hit Save and--
oh, whoops, looks like we're getting an authorization error.
And this is because, just like the real-time database,
my Cloud Firestore implementation
contains a set of security rules that determine whether or not
a certain action will be permitted.
And by default, they're set up so that nobody can
read or write to the database.
So the correct solution here would probably be
to add in some sign-in using Firebase Auth
and then create some proper, well-thought-out security rules
based on what information I'm willing to share
to each particular user.
That said, all of that would make this a much longer video
than it already is, so I'm just going to do a bit of a hack here
and make my "sampleData"
So let's head on over the Firebase Console.
I'll select my project, go here to the Database section,
and then make sure I select Cloud Firestore
from my list of database options.
Then, I will click the rules tab,
and then I'm going to add these lines here to allow reading and writing
to anything that's part of my "sampleData"
Now, this is a pretty terrible idea from a security perspective,
but at least I've contained the damage
to just what's in my "sampleData"
So I'll publish that, and we are done.
OK. Let's give it another try.
I'm going to wait a few seconds and then go back into my app.
Hit Save.
And, huh, looks like it worked!
And I can verify this by going back to the Firebase console.
We'll select the Data tab,
and then I can look at our "sampleData"
find our "Inspiration"
And sure enough, it looks like we've saved
our inspiring quote to the world.
Woo hoo.
OK. Next step, we need to show our users this inspirational quote
by grabbing that data from the cloud
and populating some kind of
Now, like the real-time database,
Cloud Firestore lets me listen to changes in the database
and update my app in real time.
But I know that, for some of you out there,
the idea of creating a real-time
is weird and strange, and sometimes
you just want a basic
So I'm going to show you how to do that first,
and then we can talk about getting fancy
with some real-time
So let's create a Fetch button.
I'm going to add some constraints and some text for the button.
It's just a button, nothing too fancy here.
And then I'm going to stick a
so that I have a user-visible location where I can display the quote.
We'll fix that up.
I'll add in some default text, give it a name.
Alright.
Next step, I'll add a placeholder method called "fetchQuote"
and I'll make that my button's
Alright. Let's edit this method.
So I already have my
as you recall, I created it as a field earlier.
So I can simply call
to fetch the document.
Now, for this call, I can also add an
And you'll notice that this time the
takes in a
A
that represents your
You can find out its ID, read some metadata about it,
make sure the underlying
and most importantly grab the data that it contains.
We'll see if our
And if it does, I'm going to get our quote by grabbing
DocumentSnapshot.getString()
And I'll use that "QUOTE_KEY" constant I created earlier.
I'll do the same thing for my "AUTHOR_NAME".
And incidentally, there are other ways of getting at this data.
If I wanted to fetch it all at once, I could call
and then that would give me
my entire
And for more sophisticated projects,
have this nifty
that will look probably familiar if you've ever used
the real-time database before.
Basically, this will take the data that you get back from the database
and attempt to create an object of the type that you specify.
It'll take whatever values it gets back from Firestore
and try to set those as fields.
So for this to work, you have to make sure
that the target object has a constructor
that accepts no arguments, and it's probably a good idea
to have getters for these fields, too.
So if I wanted to make
it might look a little something like this.
And yes, apparently it's fine
for those field names to be private like that.
And while this custom object stuff is pretty neat,
for now, I think I'm just going to stick with my original two
Next, we'll want to display the text on screen.
So let's make sure we have a field that references our quote
We can set it in
And now we're done.
And yeah, I should probably add an
but for brevity's sake, let's skip that.
So let's run this.
We'll click Fetch.
And there we go.
I can save my inspirational quote to the cloud,
and now we can load it with a click of a button.
So this is great.
We've got our data successfully saved and loaded
from Cloud Firestore.
But what if you are interested in getting your data in real time?
What if this explicit fetching seems quaint and old fashioned to you?
Well, let's show you how to get your data in real time, too.
Basically, the process is going to work nearly the same.
Let's override my
But now, instead of calling
And I'll create a new
Now, this will fetch my
but then it will also grab updated data
any time thereafter when that
So my
just like my
So in fact, I'm just going to copy the exact same code
from my
And maybe I'll print out a warning if I do get an exception, like this.
Now, while this will work, we also want to do the right thing
and turn off this
for instance when my activity is off-screen.
This will help to keep your network usage low,
which means better battery life for your user
and potentially lower database costs for you,
both of which are good things.
Now, I could track this
like in my
You see, there's an alternate call for
where you can pass in an
and this
"hey, when this
And so by specifying
my
and detach when my main activity stops.
So let me run the app again.
You can see that, thanks to my
my label automatically gets updated
with the inspirational Plato quote that was used previously.
And then, if I change my inspirational quote to this one--
I think Einstein said this--
you can see that my
without my having to even touch that Fetch button. Wow!
Gee, that was fast.
Maybe too fast, right?
It looked like my
before I even got the console message that my data was saved to the cloud.
How did that happen?
Well, what's going on is that Cloud Firestore
is making my app run as speedy as possible
by notifying me of changes that happen locally
as if they had happened on the server.
But in the meantime, it's still going ahead
and updating that data remotely in the background.
Now, it turns out my client does still receive an update
from Firestore that this data has been changed on that server.
But by default, the Firestore library will ignore this event
because with the exception of a little bit of metadata
that says, "yes, this is really from the server,"
it's identical to the first.
Now, if you wanted, you could get notified by the SDK
about this second event, the one that actually happens
when the write happens on the server.
You do this by creating some options for your
that say, "yes, notify me even when just the metadata changes."
You would pass those options in, add in a little debugging
to print out what we get, and let's add in one more quote.
Notice that when I save this thing now, my callback is getting called twice,
once for the change in my local cache and then once on the server.
Now, there may be times when this behavior is what you want,
but honestly, most of the time the default behavior works just fine.
So I'm going to go back to what we had before,
and you probably should, too.
So there you go! Congratulations!
You are now saving your data in one of the most powerful and sophisticated
online databases in the world
and using it to store two lines of text, which maybe is a little overkill.
But in a real app, you'll appreciate this power. Trust me.
Now, there's a lot more to learn about Cloud Firestore,
including more details on how that data is structured,
how
all of which are great topics for future videos.
In the meantime, go back to those security rules
and change them to be a lot more secure.
And when you're done with that, be sure to check out the documentation
and try using Cloud Firestore in your own app.
As the saying goes, "the journey of 1,000 miles
begins with reading the documentation."
I think Ruth Bader Ginsburg said that.
♪ [music] ♪