Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Getting Started With Cloud Firestore - Part Two

DZone's Guide to

Getting Started With Cloud Firestore - Part Two

Learn how to work with data to get continuous updates with Cloud Firestore, a scalable database for mobile and web app development.

· Mobile Zone ·
Free Resource

In my book The Definitive Guide to Firebase, I was all-in on chapters before the release of Cloud Firestore, so, instead of delaying the book to get a Firestore chapter in, I promised I'd write a series of blog posts introducing you to Firestore and how you can get started with using it on Android. This is part 2 in that series. You can read part 1 here.

Cloud Firestore is designed to be a flexible, scalable database for mobile, web and server development. It shares some similarities with the Firebase Realtime Database - notably keeping your data in sync across apps using realtime listeners, as well as having offline support. It's designed to let you build responsive apps that work regardless of connectivity, and which continue to give a good app experience through times of network latency. As with the rest of Firebase, it also offers integration across other Firebase and Google Cloud Platform products, including support for Cloud Functions triggers.

At the time of writing this post, Cloud Firestore is in beta release, so some details may change.

Working With Data

By the end of part one, you had written data to a Cloud Firestore containing a name and email address, which was in a document that in turn was part of a collection. The document was given an automatically generated ID, which you can see in the screenshot here:

The ID was automatically generated because the code to write the user didn't include one. For reference, here's the code:

Map<String, Object> user = new HashMap<>();
user.put("name", "Laurence Moroney");
user.put("email", "test@laurencemoroney.com");
db.collection("users")
    .add(user)
    .addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
        @Override
        public void onSuccess(DocumentReference documentReference) {
            Log.d("FirestoreDemo", "DocumentSnapshot added with ID: " + documentReference.getId());
        }
    })
    .addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            Log.w("FirestoreDemo", "Error adding document", e);
        }
    });

Should you want, instead, to add a document with a specific ID (or indeed, update an existing document with that ID), you would not add the document to the collection, but instead set the document with that ID to a new value, like this:

db.collection("users")
    .document("lmoroney1234")
    .set(user)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
             @Override
             public void onSuccess(Void thisVoid) {
                 Log.d("FirestoreDemo", "DocumentSnapshot added!");
             }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w("FirestoreDemo", "Error adding document", e);
            }
        });

Now when you inspect the data using Firebase console, you'll see a document called 'lmoroney1234' (or whatever you used) with the data:

This gives you a powerful, and flexible way to handle your data - should you want Firebase to automatically generate individual document IDs for you, use the add method to add data. Should you want to generate your own document IDs, you can do so, but you'd then use the set method to add data to that document.

In the next section, you'll take a look at reading the data.

Reading Data

In the previous section, you saw how to write and update data in the Cloud Firestore. In this section, you'll look at reading the data back into your application. There are two ways that you can do this - the first is to get a one-off snapshot of the data, analogous to a single read via a query in a traditional database, and the second is to get an ongoing stream of updates that are analogous to the Firebase Realtime database. That's part of the design philosophy behind Cloud Firestore - that it gives you the best of all worlds.

The app that you created in part 1 had a simple 'Hello World' TextView that displayed static text. In this section, you'll update that to render data that was read from Cloud Firestore.

First up, you'll need to give the TextView an ID so you can refer to it in code. This is done by editing the activity_main.xml file in the Layout folder.

Update it to look like this by adding an id attribute. The change is highlighted in bold.

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Data goes here!"
android:id="@+id/dataLabel"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toTopOf="parent" />

Now go back to your MainActivity.java and update the code to look like this:

import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;

public class MainActivity extends AppCompatActivity {
    TextView dataLabel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dataLabel = findViewById(R.id.dataLabel);
        FirebaseFirestore db = FirebaseFirestore.getInstance();
        DocumentReference docRef = db.collection("users").document("lmoroney1234");
        docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                if (task.isSuccessful()) {
                    DocumentSnapshot document = task.getResult();
                    if (document != null) {
                        Log.d("", "DocumentSnapshot data: " + task.getResult().getData());
                        String userName = task.getResult().getData().get("name").toString();
                        dataLabel.setText(userName);
                    } else {
                        Log.d("FirestoreDemo", "No such document");
                    }
                } else {
                    Log.d("FirestoreDemo", "get failed with ", task.getException());
                }
            }
        });
    }
}

In this case, a document reference is set to the specific document you wrote earlier - with the ID 'lmoroney1234.' Then, an onCompleteListener is added, which will execute upon completion of the read. If this gets a non-null document, it will read the 'name' field into a string and render it with the label. Note that documents aren't structurally enforced, so it may not have the same property as its siblings. Thus you should always check before reading it, but I've skipped over that for brevity here.

Once it reads, it's rendered in the label, as you can see here:

Getting Continuous Updates

In the previous section, you saw how to get a one-off read of the data, analogous to a query in a traditional database. But Firebase is famous for its responsive real-time updates with the Realtime database. Cloud Firestore also gives you this functionality as you'll see in this section.

To do that, use a Snapshot listener instead of an OnCreate one. This then gives you a continuous onEvent call that contains updates to the referred snapshot.

Here's the complete code for the MainActivity.java, updated for snapshot listening:

import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
public class MainActivity extends AppCompatActivity {
    TextView dataLabel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dataLabel = findViewById(R.id.dataLabel);
        FirebaseFirestore db = FirebaseFirestore.getInstance();
        DocumentReference docRef = db.collection("users").document("lmoroney1234");
        docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
            @Override
            public void onEvent(@Nullable DocumentSnapshot snapshot,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w("FirestoreDemo", "Listen failed.", e);
                    return;
                }
                if (snapshot != null && snapshot.exists()) {
                    Log.d("FirestoreDemo", "Current data: " + snapshot.getData());
                    String userName = snapshot.getData().get("name").toString();
                    dataLabel.setText(userName);
                } else {
                    Log.d("FirestoreDemo", "Current data: null");
                }
            }
        });
    }
}

Now, when you run the app, the initial result will be the same - you'll see the name rendered in the label. But, if you then go to the console and update it to a new value, you'll see the app gets updated too!

Going Further

This is just scratching the surface of what you can do with reading data. To learn more - including how you can query sets of data with 'where' type clauses, check out the documentation.

In the next post, I'll look at using Cloud Functions for Firebase with Cloud Firestore!

Topics:
mobile ,mobile app development ,android ,cloud firestore ,firebase

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}