React Native Simple Chat with Firebase and GiftedChat

Phylypo Tum
12 min readNov 15, 2018

--

This is a step by step to create React Native simple chat application using GiftedChat component with Firebase Realtime database and Firebase Storage. Firebase is Google cloud service which provide a free tier (Spark) for us to up to 100 concurrent users and 1GB file storage. The storage allow us to upload image as user avatar which GiftedChat component can display.

We will be using React Native with Expo based on this tutorial with changes to use email registration rather than anonymous and more detail on Firebase setup. Also we add the ability to upload images as an avatar.

Our app is just a simple chat program that a user can create a new login using email address. New user can register with a unique email address. If the email exists then the registration failed. User can also upload an image to create their own avatar. After the user the login, user will see the existing messages and send new messages. The new messages will be published to other users that logged in just like a group messaging in real time.

Firebase

To store and notify other user of message, we use Firebase realtime database. Firebase is a cloud service by Google that provides authentication, noSQL document databases (real time and new Cloud Firestore), storage and other services. We will only use authentication and Realtime Database and Storage. The database provides a way to subscribe to realtime events that the data is added or updated. In our use case, when someone send a message, we add the message to the database and other users that logged in can get notify of the new message. You can read more about how Firebase realtime database work in quora.

Firebase Setup

In Firebase, there is a free tier which we can use Spark plan. The limit to 100 concurrent connections. This is plenty for our uses.

Signup and logon to firebase console: https://console.firebase.google.com/

Create a new project and enter the project name.

Enable authentication using email password by clicking on Authentication, then, Sign in method, click under Status for Email/Password and click the Enable switch to on. Then click Save.

Database Setup

  • From the left menu, select database from left menu, then create a new database
  • Then select realtime database, under rule, change the read/write to true:
{
"rules": {
".read": true,
".write": true
}
}

Using Expo Snack

To code our example we will use Expo Snack which allow us to write and test code on different simulator or using our phones quickly using web browser without any setup.

First go to expo.io using your favorite browser and sign up for a free login account. Then under profile, click CREATE A NEW SNACK to start coding.

A screen with code editor will allow you to write code. It is start with cranky sandwich template.

You can click on the title and change to any name you like. Then click “Tap to play” to see how it run in the emulator.

You have the option to run this on your phone. This is prefer approach to actually see how it run on the device. But first you need to download the Expo app in your phone and sign in. Then you can connect to by clicking on Run and click on QR code. Use your QR code app on the QR code from the screen and launch it.

Let’s run the code and see the app. If you use the phone you should see the Expo logo then you app. This one is just a static screen that looks like this.

Start coding

Lets update structure of the code so we can add more screens and navigation.

Here are the description of different screens. These files are in components directory:

Login.js: screen that allow user to login with email and password input.

CreateAccount.js: allow the user to create new account. After the account is created, user have the option to upload an image as an avatar.

Chat.js: this is Gifted Chat component that display the chat message and allow user to send new messages.

The App.js will be the switch navigator.

Rename and Updating code

Rename AssetExample.js to Chat.js under components directory.

  1. Click component directory to see AssetExample.js (click again if not), move your mouse over AssetExample.js to see the the three vertical dot, and click on it then select “Rename” from the popup menu. Then enter “Chat” and press Enter.

2. Update the class to Chat in line 4 as follow:

import { GiftedChat } from 'react-native-gifted-chat';

After you press Enter, you will see a message on the bottom left screen to ask for adding the package. Just click ADD. This will add the package to your package.json.

import React from 'react';
import { GiftedChat } from 'react-native-gifted-chat';
export default class Chat extends React.Component {
render() {
return (
<GiftedChat
/>
);
}
}

3. Add new screen: Login
Under component, click + sign to add new file. Then rename “Untitled file.js” to Login.js.

4. Paste the content below:

import React from 'react';
import { Text, View, Button } from 'react-native';
export default class Login extends React.Component {
render() {
return (
<View>
<Text>My Login Screen</Text>
<Button title="Navigate to Chat"
onPress = {()=>this.props.navigation.navigate('Chat')}
/>
</View>
);
}
}

5. Update App.js to use stack navigation to route Login and Chat screen.

import Login from './components/Login';
import Chat from './components/Chat';
// Import React Navigation
import { createStackNavigator } from 'react-navigation'
// Create the navigator
export default createStackNavigator({
Login: Login,
Chat: Chat,
});

You will see the warning message below, just click ADD.

If you look at package.json, you will see something like this:

{
"dependencies": {
"react-native-paper": "2.1.3",
"react-native-gifted-chat": "0.4.3",
"react-navigation": "2.18.2"
}
}

6. Now test the navigation and make sure you see the screen.

Click Navigate to Chat, you should see a screen similar to below. As you enter the message, you will see the Send button as below.

The Send button does nothing yet. But we will work on the functionality in a bit.

At this point the base screens is ready for login functionality. So lets start with the login screen but first get information from Firebase database and create a test user.

Setup Firebase

Get Firebase configuration:

From Project Overview, click the gear, then choose Project settings.

Then scroll toward the bottom and click Add Firebase to your webapp, to get the configuration.

Copy the config section that has value of apiKey until messagingSenderId. Save this for the Fire configuration in the next section.

Manually adding new user:

Go to Authentication, then Users, now click Add user. You should see the screen below. Then enter an email and password and click Add User.

Firebase Setup

Create a new file FirebaseSvc.js by selecting App.js and click on add file icon. Then name the file FirebaseSvc.js.

Add the code below to file and replace the keys that you just copied from the Firebase settings.

import firebase from 'firebase';class FirebaseSvc {  constructor() {
if (!firebase.apps.length) { //avoid re-initializing
firebase.initializeApp({
apiKey: "<your-api-key>",
authDomain: "<your-auth-domain>",
databaseURL: "https://<your-db-url>.firebaseio.com",
projectId: "<your-project-id>",
storageBucket: "<your-storage-bucket>.appspot.com",
messagingSenderId: "<your-sender-id>"
});
}
}
login = async(user, success_callback, failed_callback) => {
await firebase.auth()
.signInWithEmailAndPassword(user.email, user.password)
.then(success_callback, failed_callback);
}
}
const firebaseSvc = new FirebaseSvc();
export default firebaseSvc;

After you enter, the code Expo knows tha we are using firebase, so there will prompt for: Add firebase to package.json? Just click ADD.

Update Login.js to read user input, set state and call the login from firebase class.

// add import
import firebaseSvc from '../FirebaseSvc';
// add state to store user input
state = {
email: 'test99@gmail.com',
password: 'test123',
};
// add login method to handle user press Login button
onPressLogin = async () => {
const user = {
email: this.state.email,
password: this.state.password,
};
firebaseSvc.login(user, this.loginSuccess, this.loginFailed);
};
loginSuccess = () => {
console.log('login successful, navigate to chat.');
this.props.navigation.navigate('Chat', {
name: this.state.name,
email: this.state.email,
});
};
loginFailed = () => {
alert('Login failure. Please tried again.');
};
// methods to handle user input and update the state
onChangeTextEmail = email => this.setState({ email });
onChangeTextPassword = password => this.setState({ password });

Test Login

Lets test the login so far. If we were to login using the created test99 account we should automatically navigate to the chat screen. Or else, we will be alert that our login is invalid. As we launch the app, we should see this screen.

Click Login, and we should see the Chat screen as below. If you modify the password to something else and click Login again, we should see the alert error.

Now we are ready to create a new login account.

Create New Login Account

With Email/Password sign-in method, you can create an account that consist of displayName, email, password, and avatar. We need displayName and avatar to display user name or image in the chat message.

When the user login, user only enters email and password, so we need to retrieve displayName and avatar from the login profile. The create account function only take email and password, so we can use updateProfile to update displayName and avatar. Avatar is the url to the image that we stored in Firebase storage. We will cover this in the upload image section

To create an account, we use the create account function createUserWithEmailAndPassword which takes in email and password.

firebase.auth()
.createUserWithEmailAndPassword(user.email, user.password)

When create account is successful we use the updateProfile to update displayName or avatar. Now we will update user displayName like this:

var userf = firebase.auth().currentUser;
userf.updateProfile({ displayName: user.name})

Lets add method createAccount in FirebaseSvc.js to create new login.

createAccount = async (user) => {
firebase.auth()
.createUserWithEmailAndPassword(user.email, user.password)
.then(function() {
var userf = firebase.auth().currentUser;
userf.updateProfile({ displayName: user.name})
.then(function() {
alert("User " + user.name + " was created successfully.");
}, function(error) {
console.warn("Error update displayName.");
});
}, function(error) {
console.error("got error:" + error.message);
alert("Create account failed.");
});
}

Create a new screen to allow user to create an account by duplicate the Login.js. Hover the mouse over Login.js and click the three vertical dots and choose Duplicate. Name the new file: CreateAccount.js. Then update the class name and change login method with create account as follow.

To highlight some of the code, when user click on Create Account, we call createAccount in firebaseSvc. We will cover that below. When user click on upload, we need permission to open the CAMERA_ROLL then get the image user select and scale the image to max 150px width. Then call the uploadImage in the firebaseSvc. Lets cover the firebaseSvc changes. For the createAccount, we use createUserWithEmailAndPassword method from firebase.

createAccount = async (user) => {
firebase.auth()
.createUserWithEmailAndPassword(user.email, user.password)
.then(function() {
var userf = firebase.auth().currentUser;
userf.updateProfile({ displayName: user.name})
.then(function() {
alert("User was created successfully.");
}, function(error) {
console.warn("Error update displayName.");
});
}, function(error) {
alert("Create account failed. Error: "+error.message);
});
}

Adding avatar

To add an avatar, you will need to get a url of the user image. We will allow user to upload an image from their photo library or camera. The image will be upload to Firebase Storage which is a file storage in the cloud.

To upload the image, we use the put method on the uri that was passed in. The uri looks something like: file:///Users/sim1/Library/…ImagePicker/…0E2929B.jpg. The detail uploadImage method in firebaseSvc.js is below:

uploadImage = async uri => {
try {
const response = await fetch(uri);
const blob = await response.blob();
const ref = firebase.storage().ref('avatar').child(uuid.v4());
const task = ref.put(blob);
return new Promise((resolve, reject) => {
task.on('state_changed', () => { }, reject,
() => resolve(task.snapshot.downloadURL));
});
} catch (err) {
console.log('uploadImage error: ' + err.message);
}
}

When user successfully upload the image, the state.avatar is updated. Then call to updateAvatar to update user profile with the url. UpdateAvatar is similar to update the user displayName. We will not cover the detail here. Both displayName and avatar is used in the GiftedChat component. If avatar is not available, it uses the displayName to create the initial for the user.

In App.js, we also need to add this screen to the navigation route.

import CreateAccount from './components/Chat';...
export default createStackNavigator({
Login: Login,
Chat: Chat,
CreateAccount: CreateAccount, // add new route
});

Final Test

We are ready to test the create account and upload image. Launch the app, then click “Go to create account”, then change the email and name, click “Create Account”. If the email exists, it will fail else it should say account created successfully.

Now we can upload the image by clicking “Upload Avatar Image”. For the first time it will prompt for permission to the image. Clicking OK to start choosing the image.

Select an image and click Choose. Now you should be alert that the image is successful.

Verify that the image is saved in the Firebase storage by going to Firebase console. Notice that the output of the image in the output log:

The file is in avatar folder with the name start with 983d9217. Notice that the url also contain token to access the file.

Update Chat Message

We are ready to show the update the chat message. The Chat component will tie to this.state.message which is an array of object.

In Chat.js we need the user info to create a message. We can use the prop that pass in from the Login screen. We create message array in the state. When the state get updated and GiftedChat will refresh.

The messages are updated when the chat component is mounted by calling to the firebaseSvc to refOn method.

componentDidMount() {
firebaseSvc.refOn(message =>
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, message),
})
)
);
}

In the refOn method inside firebaseSvc is follow:

refOn = callback => {
this.ref
.limitToLast(20)
.on('child_added', snapshot => callback(this.parse(snapshot)));
}

The parse method take the snapshot data and construct a message:

parse = snapshot => {
const { timestamp: numberStamp, text, user } = snapshot.val();
const { key: _id } = snapshot;
const timestamp = new Date(numberStamp);
const message = {_id, timestamp, text, user};
return message;
};

To send a message, we call the send method from GiftedChat component in onSend property as such: onSend={firebaseSvc.send}

The send method in Firebase.js is:

send = messages => {
for (let i = 0; i < messages.length; i++) {
const { text, user } = messages[i];
const message = {text, user, createdAt: this.timestamp, };
this.ref.push(message);
}
};

Test Chat

We are set to go. Lets test by login and send a message. As you type the message, the Send button will be enable. After clicking Send the message should show up on the right.

Verify the message in Firebase console

In the Firebase console, select database from left menu, then Realtime Database.

You would see the Messages, then expand object and you see the detail of the message and the user.

Here is the full Chat.js .

This is it. You see the full code and try this code in repo snack here:

https://snack.expo.io/@samauch/simple-chat-with-avatar

Thanks for reading. See other related tutorial below.

References

  1. https://blog.expo.io/how-to-build-a-chat-app-with-react-native-3ef8604ebb3c
  2. https://appendto.com/2017/11/build-simple-chat-app-react-native-firebase/
  3. https://github.com/CodeLinkIO/Firebase-Image-Upload-React-Native

--

--