Recently, while working on a social media application at Gurzu, we built a chat feature as part of its ecosystem. With time constraints, we quickly needed a simple chat system for the users with little to none time left to spare for building an entire server-side code. Already having an authentication system in place, we decided to integrate Firebase with our existing system and leverage Firebase’s real-time features, such as Realtime Database or Cloud Firestore, while still using our own authentication infrastructure.
Part 1: Chat Model
Let’s first look into how we set things up for Firestore and defined the chat model that we worked with.
Setting up Firestore
Since the application was already configured with Firebase, we had access to all the other build product categories of Firebase. To set up a Firebase project, visit the Firebase console and configure the project with everything that is required to use the different firebase services. Once all the setup steps were done, we enabled Cloud Firestore and its database security rules.
Structuring the Database Model
That’s it for the setup part, now let’s look into how we actually structured the database and built the application.
Firestore is a NoSQL document-based realtime database that is offered as part of the Firebase platform. Firestore stores data in collections and documents, and it allows for real-time synchronization and querying of data. Firestore is used in our chat feature to store all the text messages, conversations threads, and user details and the notifications are sent through firebase cloud messaging which are triggered with the help of a cloud function deployed to firebase cloud functions. We created three collections (namely conversations, messages and users) to store the chat data, each containing its own set of documents.
The “users” collection contains a document for each user that includes their full name and url for their profile picture. The documentId for each of the user documents in this collection is the respective user’s userId that is stored in the app’s main database. The collection gets populated as the number of users in our app increases.
The table below is a list of the fields of a document in the users collection:
The “conversations” collection contains a document for each conversation that includes the conversation ID, type (individual or group), name (if the conversation is a group conversation), an array of userIds for the participants in the conversation, userId of the creator, the recent most message in the conversation, and an array of members that have read the recent most message. The documentId in this collection for conversation threads of sessions are set as their respective sessionIds and for other conversations the documentIds are automatically generated by firestore.
The table below is a list of the fields of a document in the conversations collection:
The messages collection contains all the messages that are sent out from all the users of the mobile app. A single document represents a single message and contains fields for storing the conversation reference i.e. the conversation thread to which the message belongs, the message text, the type of message sent i.e. photo, text or activity, and the time at which the message was sent. The documentIds in this collection are automatically generated by firestore.
The table below is a list of the fields of a document in the messages collection:
Part 2: Chat Functionality
Now that we’ve gone through how we’ve structured our firestore database model, let’s look into how we’ve created the whole chat feature.
Building the Chat UI
The user interface for the chat feature was developed using the Gifted Chat library. It is a React Native library for building chat interfaces and saves us a lot of time in creating the Chat functionality and flow. It provides a set of customizable UI components that can be easily integrated into a React Native application to create a complete chat interface that looks and feels like a messaging app. The final design and flow of our chat feature came to look something like this where the messaging screen was completely created by customizing the gifted chat library.
The user flow involves users seeing the list of their chats where they can select a chat or create a new conversation with another user they want to chat with, and navigate to the Chat Screen where all the messaging happens.
Implementing Chat Functionality
With the chat components built and authentication done through the backend service, we moved ahead by adding the chat functionality itself. We started by importing the database object from the firebase config file and other methods from firebase/firestore to fetch and add data to the collection. We’ve explained the chat functionality in detail with our high level chat flow diagram below.
Let’s go through the key tools that we’ve used for our complete chat feature.
Firebase Cloud Functions
Firebase Cloud Functions is a serverless compute service provided by Firebase. It allows developers to write and deploy server-side code that can respond to events from various Firebase services, such as Firestore, Authentication, and Cloud Messaging. By using Firebase Cloud Functions in the chat feature, developers can ensure that push notifications are sent quickly and efficiently to users, without the need for maintaining and managing server infrastructure.
We’ve used Firebase Cloud Functions to send push notifications to users when a new message is sent. When a new message is written to the Firestore database, a Cloud Function is triggered to send a push notification to the intended recipient(s) of the message.
Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that allows developers to send notifications and messages to users on Android, iOS, and web platforms. FCM is used in the chat feature to send push notifications to users when they receive a new message.
Amazon S3 (Simple Storage Service) is a cloud-based object storage service provided by Amazon Web Services (AWS). S3 allows developers to store and retrieve files, images, and other objects, and it provides high durability, availability, and scalability.
The images sent out by users as part of a conversation are stored in an S3 bucket. The URLs of those images are then saved in the project’s Firestore database in the images field of their respective message documents in the messages collection.
Storing and Rendering Chats
Once the logic for the chat flow was defined, we started by storing messages and conversations, and rendering them into our screens. The cloud firestore provides us different methods to get by with querying, reading, writing and removing chat data. You can go through the cloud firestore documentation to get a better understanding of its usage.
We queried the Firestore database in the inbox screen and displayed all the conversations that the logged in user is involved in. The React component keeps track of changes in Firestore database’s “conversations” collection through a listener and updates the state accordingly. Then, it retrieves conversation data from Firestore based on the current user’s ID and sorts it by the most recent message to render the user’s conversations in the user interface.
Similar to the inbox screen, this screen’s react component retrieves chat messages related to a specific conversation from the Firestore collection “messages” and updates the component’s state with the retrieved data to render messages into our customized Gifted Chat component. The messages are sorted by creation timestamp in descending order, and a limit of 15 messages is set for each paginated retrieval. Additional operations on the conversation document, such as updating the read status, recent message and fetching sender information are also done here.
On sending a new message in the conversation, we’ve redeclared the onSend function for Gifted Chat to create a new document in the “messages” collection referencing that particular conversation and define the messageType. Also, users can delete their messages, for which we’ve just deleted the message document itself and then updated the details of that particular conversation. And, for any activity that takes place in the conversation, we create a new message document with the type activity and render it as a message in the chat screen.
New Conversation Screen
To initiate a new conversation with other connections on the app, users select a connection to chat with and then a new document in the “conversations” collection gets created with all the necessary information.\ In conclusion, integrating a chat feature into our React Native app using Firebase has been a game-changer for user engagement. With Firebase’s real-time capabilities, we were able to quickly set up a scalable chat system without spending valuable time on server-side code. By structuring our database with collections for users, conversations, and messages, we easily stored and synchronized chat data. The Gifted Chat library provided intuitive UI components, saving us precious development time. With Firebase Cloud Functions and Cloud Messaging, we efficiently sent push notifications to users for new messages. Our app now offers seamless messaging, allowing users to connect and interact in real-time.
- React-native-firebase docs
- Cloud Firestore Usage
- Gifted Chat Library
- Build a Chat App with Firebase and React Native
- Add A Chat To Your React Native App With Firebase And Firestore
From concept to deployment, Gurzu provides an end-to-end mobile development solution.