Saving data from the Data Center to a Cloud Storage

This is the third section in the series of modifying and getting started with the Theta Client and React Native Demo.

In the last article in this series, we learned how to store data offline (locally) using the async storage package. In part 3 of the series, we will learn how to store data from the Data Center Component to the Cloud. We do not just want to save data locally; we want to sync it with the cloud. One thing about storing data using the async storage package, as we did in the last article, is that it has some limitations when considering factors like “size of data.” Async storage helps us persistently store data in a key-value pair, but it works best when dealing with small amounts of data.

For this guide, we will implement Firebase as a cloud service to handle data storage. Using Firebase, we can overcome the limitations of local storage and ensure our data is secure, scalable, and accessible from anywhere. Firestore, a NoSQL database provided by Firebase, will be our choice for storing and syncing data in real-time, which will significantly enhance the functionality and reliability of our application.

What is Firebase?

Firebase is a platform developed by Google for creating mobile and web applications. It provides a suite of cloud-based tools and services that help developers build, improve, and grow their apps. Firebase is known for its real-time database, authentication services, cloud storage, analytics, and a variety of other tools that streamline the development process. It is designed to handle backend infrastructure, enabling developers to focus on creating great user experiences without worrying about server management.

What is FireStore?

Firestore, also known as Cloud Firestore, is a scalable and flexible NoSQL cloud database provided by Firebase. It allows developers to store and sync data between users and devices in real time. Firestore supports rich queries, atomic transactions, and offline data persistence, making it an ideal choice for applications that require real-time data synchronization and offline capabilities. With Firestore, data is organized into documents and collections, allowing for a more structured and easily accessible format compared to traditional databases.

This guide mainly focuses on modifying the present Data Component, not the UI logic but the Data Storage logic, which currently uses async storage.

Without further ado, let’s get into the nitty-gritty of this guide.

This guide is divided into five sections:

  1. Setting up Firebase and FireStore
  2. Initializing and Installing Firebase into the Project
  3. Implementing the FireStore to handle the data storage for our app
  4. Saving/Getting data from FireStore
  5. Testing that the data gets saved to the firestore

Setting up Firebase

  1. Navigate to Firebase’s official website by querying your browser with “Firebase”, sign up, and log in.

  2. Setting up the Project
    a. Click on the Firebase console in the header to open it, then create a new project.


    b. Click on the “Create a project” button.
    c. Add a name for your project, then click continue.
    d. On the next page, turn off analytics.
    e. Wait for the new project to be bootstrapped. Click continue when it is done.

    f. Add an app to get started. Since we are dealing with Android, we must use the Android app option when adding a new app. Hover on all the option icons, then click on the one for Android–the second from the left after the “ios” icon.


    In the “add app” interface, we are expected to fill out some input fields.

  3. Adding Firebase to your Android app
    Filling out the input fields for the “add app” session.
    a. Inputting the Android package name: To get the Android package name, navigate like this: demo-react-native > android > app > src > main > java > com > demoreactnative > MainApplication.java


    The package name is “com.demoreactnative.” Go back to Firebase and add it. You can then add an app name or leave it blank. Finally, click on “Register app.”

  4. Download and then add Config file


    a. Download the google-services.json file from the Firebase Console.
    b. Place this file in the /android/app directory of your React Native project.
    Note: To speed things up, you can download the file directly to the app directory, a subdirectory of the android directory.
    c. Click on next to skip the Firebase SDK plugins.

Initializing and Installing Firebase into the Project

  1. Query your browser to search for “react native firebase”, then click on the first link.

  2. Install the React Native Firebase “app” module at the root of your React Native project—”demos/demo-react-native”—with NPM or Yarn.

    # Using npm
    
    npm install --save @react-native-firebase/app
    
    # Using Yarn
    
    yarn add @react-native-firebase/app
    

    Here, we are using yarn.

  3. Place the google-services.json file from the Firebase Console into /android/app. Thus, the google-services.json file location becomes /android/app/google-services.json.

  4. Navigate to the demo-react-native directory and first access the build.gradle file in the app’s android root folder—demo-react-native`.

Add the following classpath to the dependencies section of the /android/build.gradle file:
classpath 'com.google.gms:google-services:4.4.2'

Save it to reconfigure the build file.

Now, to the /android/app/build.gradle file,

Add the Googe service plugin to the top of the file:

apply plugin: 'com.google.gms.google-services'

Save it to reconfigure the build.

Recap

  • Add the Google Services classpath dependency in android/build.gradle.

  • Apply the Google Services plugin in android/app/build.gradle

Now that we have everything all set run:

yarn android

This will reconfigure the whole build since a lot has been changed.

Implementing the FireStore to handle the data storage for our app

  1. Go back to the Firebase Console and access the Firebase Database - Firestore from the “Build” dropdown.

  2. Click on the “Create database” button. Follow the instructions thereafter and choose the default location. For this guide, I chose the “test mode” option instead of “Production mode.” Finally, click “Create,” and your Firebase Database will be created.

  3. Start a collection inside FireStore itself. Give it a collection ID named “Cars” since that is what we are dealing with. Then, fill out the input fields of the Collection. You can add a Document ID or use the “auto-id” option from FireStore, which I used. Now, create a sample document under the Collection. Choose a field—“vehicleId”, “make”, “model”, “location”, “notes”, “imagePath”.

  4. Save and view the dashboard.


    Next, we want to be able to do what we just did above directly from the code, in the sense that once the user clicks on the “save” button in the Data Center form, the data automatically gets saved in our FireStore Collection.

  5. Install FireStore into your React Native app.

    yarn add @react-native-firebase/firestore
    

Saving/Getting data from FireStore

First, let us try to get the data we manually added to firestore.

  1. Fetching the data currently in the Collection
    A. Initialize firestore in the project. To test faster, let’s use a screen we can easily access: the home screen, embedded in the MainMenu.tsx file.
    Note: When I installed firestore directly from @react-native-firebase/firestore, I had an error saying firebase wasn’t initialized then I went ahead to create a firebase.js file inside the src directory. This firebase.js file embeds the needed data for firebase configuration. Here is the content of the file:

    import firebase from "@react-native-firebase/app";
    import firestore from "@react-native-firebase/firestore";
    const firebaseConfig = {
      apiKey: "AIzaSyB2UodoTKyocvIG0ZVyRh6EvkHKyQEFGM0",
      projectId: "ricoh-theta-9e2cb",
      appId: "1:13705834856:android:c3b2731db6e8a29aa426dd",
      storageBucket: "ricoh-theta-9e2cb.appspot.com",
    };
    
    if (!firebase.apps.length) {
      firebase.initializeApp(firebaseConfig);
    }
    
    export { firebase, firestore };
    

    Again, I got another error that the config data isn’t complete since it lacks a databaseURl–this isn’t needed since we are using firestore, but it was requested, so I created a dummy url using my projectId.

    Updated the firebaseconfig object to this:

    const firebaseConfig = {
      apiKey: "AIzaSyB2UodoTKyocvIG0ZVyRh6EvkHKyQEFGM0",
      authDomain: "ricoh-theta-9e2cb.firebaseapp.com",
      projectId: "ricoh-theta-9e2cb",
      storageBucket: "ricoh-theta-9e2cb.appspot.com",
      messagingSenderId: "13705834856",
      appId: "1:13705834856:android:c3b2731db6e8a29aa426dd",
      databaseURL: "https://ricoh-theta-9e2cb.firebaseio.com", // Dummy URL
    };
    

    And finally, no more errors.

    In addition, you can get the values for the firebaseconfig object from both the google-services.json file and the app section in the project settings.

    B. Next, recall that we are using the first screen of the app demo to access the data in the Cars collection. Navigate to the MainMenu.tsx file and create an asynchronous function to fetch the data.
    First, import firestore from the firebase.js file:

    import { firestore } from './firebase';
    

    The asynchronous function:

    const fetchData = async () => {
        try {
          const carCollections = await firestore().collection("Cars").get();
          if (carCollections.empty) {
            console.log("No matching documents.");
            return;
          }
          carCollections.forEach(doc => {
            console.log(doc.id, "=>", doc.data());
          });
        } catch (error) {
          console.error("Error fetching data: ", error);
        }
     };
    

    Note that in FireStore, data is stored using a document (doc for short) embedded in a collection. A collection can have multiple documents, and a document can also have a collection.

    Function Definition:
    The function fetchData is defined as an asynchronous function using the async keyword. This allows the use of await inside the function to handle asynchronous operations more naturally.

    a. Fetching the Collection:

    const carCollections = await firestore().collection("Cars").get();
    

    Explanation: This line initiates a query to Firestore to get all documents in the “Cars” collection.
    Await: The await keyword pauses the execution of the function until the promise returned by firestore().collection("Cars").get() resolves.
    Result: The result, carCollections, represents the query snapshot, which contains all the documents retrieved from the “Cars” collection.

    b. Iterating Through the Documents:

    carCollections.forEach(doc => {
        console.log(doc.id, "=>", doc.data());
    });
    

    Explanation: If the “Cars” collection is not empty, this line iterates through each document in the carCollections query snapshot.
    Action: For each document, it logs the document’s ID and data to the console. The doc.id represents the document’s unique identifier, and doc.data() retrieves the data stored in the document.

    C. Now, let’s run the fetchData function by embedding it inside theuseEffect. This will automatically trigger the function once the component mounts.

    React.useEffect(() => {
        const subscription = AppState.addEventListener(
          'change',
          handleAppStateChange,
        );
        fetchData();
        return () => {
          subscription.remove();
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);
    

    Reload the metro terminal and ensure you are in the MainMenu.tsx file. You should get the current data in the Collection logged out.

Now that we see that Firebase and firestore work ideally in our build, as we have tested it in getting data from firestore (firebase database), you can clean the firestore code (the import and the asynchronous function) in the MainMenu.tsx file.

  1. Saving data from the Data Center to the Firestore
    Here, we are dealing with the DataCenter.tsx file. When fetching the data, we used the get() method that firestore provides. Now that we want to add data to the database, we will use the add() method.
    Update the saveData function from this, which is presently using asyncstorage:

    const saveData = async () => {
      const data = {
        vehicleId,
        make,
        model,
        location,
        notes,
        imagePath,
      };
    
      try {
        const jsonValue = JSON.stringify(data);
        console.log(jsonValue);
        await AsyncStorage.setItem(`@vehicle_${vehicleId}`, jsonValue);
        Alert.alert("Data saved successfully");
        navigation.goBack();
      } catch (e) {
        console.error("Error saving data", e);
        Alert.alert('Failed to save data’);
      }
    };
    

    To this:

    const saveData = async () => {
      const data = {
        vehicleId,
        make,
        model,
        location,
        notes,
        imagePath,
      };
    
      try {
        await firestore().collection("Cars").add(data);
        Alert.alert("Data saved successfully");
        navigation.goBack();
      } catch (e) {
        console.error("Error saving data", e);
        Alert.alert("Failed to save data");
      }
    };
    

    Import firestore at the top of the DataCenter component

    import { firestore } from './firebase';
    

    In the function above, we use the add() method to push data from the Data Center to the Cars collection. Whenever the button that triggers the saveData function is clicked, any new data submitted will create a new document under the Cars collection and add the data.

Testing the new Storage Method

  1. Click on the “list photos” button to launch the Phot list page
  2. Click on the download button for a particular photo, then wait for the alert that the download was successful. You will then be taken to the Data Center.
  3. Fill out the input fields in the Data Center and save them.
  4. Next, head to the firestore and check if the newly added data has been added to your Cars collection.

Conclusion

This guide explored how to store data from our React Native app seamlessly in Firestore. We transitioned from using AsyncStorage to leveraging Firestore’s robust and scalable cloud storage solutions. By following the outlined steps, you can save, retrieve, and manage your app’s data more efficiently using Firestore. This integration enhances data persistence and allows real-time synchronization and advanced querying capabilities, ultimately leading to a more dynamic and responsive user experience.

If you enjoyed this, please like, share, and comment!

2 Likes

Thank you for this article.

Suggest you obscure the actual API keys from your article for security purposes. Hopefully, you didn’t use the real API key in the article.

What is the free storage usage limit for Cloud Storage for Firebase?

Firebase Pricing

Did you have to put a credit card in?

If you have a credit card in the system, suggest you shutdown the project and change your API key (delete access)

Thanks for the information, I really appreciate. I will remove the API key and confirm if I have any card in the system.

it’s best to shutdown down the API key either way to protect your account. Just in case you want to use AWS in the future. In the future, avoid publishing any API key.

Also, if you have a public GitHub repository, do not have code with a public API key in it at any point. Meaning that git will keep a record of old commits. if the key is in your code, you need to save it in .env and put .env in your .gitignore.

This is for the future.

Yes, I will shut it down right away. I don’t know what happened but I normally store sensitive things like this in my .env file which won’t be pushed to GitHub. I’d be more careful next time. Thank you for the observation.

for a tutorial, what you can do one of two things:

  1. use a live key and then delete or shutdown the key prior to publishing the tutorial (reader has to use their own key)

OR

  1. if it is a live site, delete a portion of the key and fill it with XXXXX or something similar

This is to protect you, the author.

BTW, as you’re online now. Are you interested in a short video chat on Google Meet? I was going to introduce you to some computer science interns we have in California, including @caitlyn who goes through your article. maybe @erikrod1 as well from Silicon Valley. if not, we can try talk next week. There’s another guy @Phat_Ca who is also studying CS.

For the next project, we would like to discuss building a 360 image gallery with these components

  • Django
  • Django REST Framework
  • React to display images
    • use Vite to start React project, not Webpack
    • use MUI since you already used React Native Paper for another article
      • you could use another UI toolkit if you propose it. I’m not really biased to use MUI
    • first try and get Django to serve up React, but if it’s not easy, then use another Vercel container to host the React app.
  • image storage on AWS S3
    • do not deploy live set with your own S3 keys, we will redeploy S3 keys with Oppkey keys once project is completed
    • do not show your S3 key in the tutorial. obscure the key

This is a full-stack example that covers both the backend and frontend as well as hosting.

Once the project is completed, we will make a private copy of the project and redeploy with Oppkey AWS key for the S3 storage


Integration of React and Django

I have looked at a number of ways to integrate React and Django. At the moment, I am running React as a separate app on Digital Ocean and Django on AWS App Runner. I have not been able to adequately look at the strategies below.

django-vite

django-vite-plugin

django-breeze

Testing production build of django-breeze locally.

./manage.py runserver --insecure

other

Thanks a lot, I am quite tired this night and going to bed early. I hope you understand. I am super pumped to meet with the fellow CS interns. We can have the meeting tomorrow if that’s fine with them as I am eager to meet all of them. Thank you!

Wow, thanks a lot from the various guides. I’d check them out and see how to structure the next post. I will definitely reach out to update you and if I need clarity on any aspect.

sent you direct email for video meeting on Monday with the US interns.

The other interns are experienced with Django and some are also experienced with React. Maybe they can help if you have questions in the future.

Thank you! I got the mail and I am looking forward to the meeting on Monday.

2 Likes

I am getting started with it. It’s been a while and I have been a bit down and settling some things. Thank you!

Thanks for the resources.