Skip to main content

Building a Chat App with React Native and Expo (Part 3)

· 11 min read

Building a Chat App with React Native and Expo (Part 3)

In this tutorial series, I'll be showing you how to build a functional and secure chat app using the latest React Native libraries, including Gifted Chat and the Expo framework powered by the ChatKitty platform.


In the second article of this series, you learned how to use the Gifted Chat React Native library with ChatKitty's JavaScript SDK to build a full featured chat screen with real-time messaging functionality into your app. You also added screens for users to create public channels, discover new channels, and view their channels.

In this tutorial, you'll be using Expo push notifications and ChatKitty Chat Functions to set up local notifications and push notifications to inform users when new messages are received or relevant actions happen inside a channel and across your app.

You can checkout our Expo React Native sample code any time on GitHub.

After reading this article, you will be able to:

  1. Implement local notifications for users to see what's happening from another screen

  2. Use ChatKitty user properties to store arbitrary data related to your users like expo push tokens

  3. Use Expo push notifications and ChatKitty Chat Functions to implement push notifications

If you followed along the last article, you should already have the ChatKitty JavaScript SDK NPM package added to your Expo React Native project.


Before we begin, let's go over some terms we'll be using a lot in this article.

What are local notifications?

Local notifications are messages that pop up while your app is in-use to inform a user of relevant actions related to another screen in your application from their current screen. ChatKitty sends notifications to your app through the ChatKitty JavaScript SDK. You can listen for these notifications and use them to build in-app notification views.

What are push notifications?

Push notifications are short messages sent to mobile devices to alert a user when something of interests happen, and provide information related to that event even when your app isn't currently in-use. Push notifications are a great way to engage your users and improve your customer experience. Push notifications are a critical part of most chat apps and have traditionally been difficult to implement. However, the Expo framework provides seamless support for push notifications, simplifying the process of send push notifications to your users.

Installing notification libraries

For this project, you'll be using Expo push notifications. So, install the Expo notifications, and other dependency modules you'll need to get expo push tokens which are needed to register user devices for push notifications:

npx expo install expo-device expo-notifications

Let's also install the EAS CLI. EAS Build is a hosted service for building app binaries for your Expo and React Native projects. You will be using it to set up and handle Expo push notification credentials.

npm install -g eas-cli

Setting up Expo push notification credentials

info

Check out the official Expo push notifications guide for more information on setting up Expo push notification as things change,

For iOS, the managed Expo workflow handles push notification credentials automatically when you register your device and run the eas build command. However, for Android you'll need to add an Android app to your Firebase project, update your project, and upload your FCM server credentials to Expo.

Adding Firebase credentials to the app

From the Firebase console side menu, go to your "Project settings".

Screenshot: Firebase project settings

Go to the "Your apps" section and click the Android icon:

Screenshot: Screenshot: Firebase add app

Fill out the application details and register your android app

Screenshot: Screenshot: Firebase create android app register

Download the google-services.json file and add it to your Expo React Native project's root directory

Screenshot: Screenshot: Firebase create android app download

In your app.json inside your project's root directory, add an android.googleServicesFile property with the relative path to the google-services.json file, as well as an android.package property with your app's Android package name:

{
"expo": {
...
"android": {
"package": "com.yourpackage.yourcoolapp",
"googleServicesFile": "./google-services.json"
}
...
}
}

Uploading FCM Server Credentials to Expo

To allow Expo to send push notifications to your Android app, you'll need to upload your FCM server key. Before you can upload your server key to Expo, you'll need to create an Expo account.

To get your FCM server key, go to "Project Settings" section of your Firebase project, then go to the "Cloud Messaging" tab. As you will see, Server Key is only available in Cloud Messaging API (Legacy), which is disabled by default, so you will need to enable it.

Screenshot: Enable Firebase Cloud Messaging API

Once you have enabled this, you can copy the server key listed next to the token.

Screenshot: Firebase Cloud Messaging server key

Now, go to the "Credentials" section under the "Account Settings" option from your Expo account's dashboard side menu, and upload the required credentials to Expo.

To run a local Android build, you will need to run this command as well:

eas build --platform android --local

Getting a user's expo push token

To send a push notification to a user using Expo, we'll need their expo push token. Once we get the expo push token, we can then store it as a ChatKitty user property, so we can access it later in a chat function or on a back-end.

We'll interface with Expo notifications using a new context provider. Inside the src/context/ directory, create a new file notificationProvider.js. We'll define a new context and provider component to register the user's device for push notifications and update the current ChatKitty user's properties to store their Expo device token.

src/context/notificationProvider.js
import { createContext, useEffect, useRef, useState } from 'react';
import { Platform } from 'react-native';

import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import { chatkitty } from '../chatkitty';

Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false
})
});

export const NotificationContext = createContext({});

export const NotificationProvider = ({ children }) => {
const [notification, setNotification] = useState(null);
const notificationListener = useRef();
const responseListener = useRef();

useEffect(() => {
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
setNotification(notification);
});

responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
console.log(response);
});

return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);

return (
<NotificationContext.Provider
value={{
notification,
registerForPushNotifications: async () => {
let token;
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
console.log(token);
} else {
alert('Must use physical device for Push Notifications');
}

if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C'
});
}

await chatkitty.updateCurrentUser((user) => {
user.properties = {
...user.properties,
'expo-push-token': token
};
return user;
});
}
}}
>
{children}
</NotificationContext.Provider>
);
};

Later, we'll be updating the notification provider to send local notifications.

To get the notification context inside your app components, wrap the app routes with notification provider.

Edit the src/context/index.js file to wrap the app routes with the notification provider.

The index.js file should now contain:

src/context/index.js
import React from 'react';
import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';

import { AuthProvider } from './authProvider';
import { NotificationProvider } from './notificationProvider';
import Routes from './routes';

export default function Providers() {
return (
<PaperProvider theme={theme}>
<AuthProvider>
<NotificationProvider>
<Routes />
</NotificationProvider>
</AuthProvider>
</PaperProvider>
);
}

const theme = {
...DefaultTheme,
roundness: 2,
colors: {
...DefaultTheme.colors,
primary: '#5b3a70',
accent: '#50c878',
background: '#f7f9fb'
}
};

Next, update homeStack.js to call registerForPushNotifications from the notification context.

src/context/homeStack.js
import React, { useContext, useEffect } from 'react';
import { NotificationContext } from './notificationProvider';

export default function HomeStack() {
const { registerForPushNotifications } = useContext(NotificationContext);

useEffect(() => {
registerForPushNotifications();
}, []);

// Unchanged
}

With that, you should have the user's expo push token as the expo-push-token user property. With Expo set up, let's create a ChatKitty chat function to use Expo to send a push notification when a ChatKitty notification event happens.

Adding Expo to your Chat Runtime

ChatKitty makes it easy to integrate your back-end and external services like Expo into a ChatKitty application using Chat Functions. Chat Functions let you write arbitrary code that runs any time a relevant event or action happens inside your app. We'll be using a chat function to send a push notification whenever an event occurs that a user should be notified about, and the user isn't online. With ChatKitty, you can use any NPM package inside your Chat Functions as a Chat Runtime dependency.

From your ChatKitty application dashboard, go to the "Functions" page:

Screenshot: ChatKitty side menu functions

Go to the "Runtime" tab and add a new dependency to the Expo Server SDK NPM package, expo-server-sdk. Version 3.7.0 was the latest version as of the time this article was written.

Screenshot: ChatKitty runtime add expo Remember to click the "Save" icon to confirm your chat runtime dependencies changes.

Now we're ready to define a chat function to send a push notification using Expo, whenever a user should be notified about an event, and the user is offline.

Sending push notifications using a chat function

From your ChatKitty application dashboard, go to the "Functions" page and select the "User Received Notification" event chat function:

Screenshot: ChatKitty chat functions

This chat function runs whenever an event a user can be notified about happens. Edit the chat function to send a push notification if the user isn't currently online.

const { Expo } = require('expo-server-sdk');

const expo = new Expo(); // create Expo client

async function handleEvent(
event: UserReceivedNotificationEvent,
context: Context
) {
if (event.userHasActiveSession) return; // skip if this user is online

const expoPushToken = event.user.properties['expo-push-token']; // get the expo push token registered

if (!expoPushToken || !Expo.isExpoPushToken(expoPushToken)) return; // check expo push token is present and valid

const notification = event.notification;

// send push notification with Expo
await expo.sendPushNotificationsAsync([
{
to: expoPushToken,
sound: 'default',
title: notification.title,
body: notification.body,
data: notification.data
}
]);
}

Screenshot: ChatKitty chat function user received notification Remember to click the "Save" icon to confirm your chat function changes.

If you close the app now, and send a message from another device as another user, you should see a push notification:

Screenshot: Push notification

Handling local notifications with Expo

Now that we have Expo push notifications set up, let's also handle local notifications with Expo.

To send local notifications, let's add a sendNotification function to notificationProvider.js that schedules a local Expo notification and export it in the notification context:

src/context/notificationProvider.js
export const NotificationProvider = ({ children }) => {
// Unchanged...

return (
<NotificationContext.Provider
value={{
notification,
sendNotification: async (content) => {
await Notifications.scheduleNotificationAsync({
content,
trigger: null
});
},
registerForPushNotifications: async () => { /* Unchanged */}
}}
>
{children}
</NotificationContext.Provider>
);
};

Next, in homeStack.js register a ChatKitty onNotificationReceived event listener in the useEffect React hook to show received in-app notifications:

src/context/homeStack.js
import {chatkitty} from '../chatkitty';

export default function HomeStack() {
const {registerForPushNotifications, sendNotification} = useContext(NotificationContext);

useEffect(()=> {
registerForPushNotifications();

chatkitty.onNotificationReceived(async (notification) => {
await sendNotification({
title: notification.title,
body: notification.body
});
});
}, []);

// Unchanged...
}

Conclusion

Pretty cool, you've completed the third part of this tutorial series and successfully implemented push notifications, using the Expo framework and ChatKitty Chat Functions. You've also implemented local notifications that seamlessly inform your users when something they care about happens. Your users are now always in the loop.

What's next?

In the next post of this series, we'll be enhancing your chat app's user experience with direct messaging, typing indicators, and chat presence notifications. Stay tuned for more. 🔥

If you have any questions, comments or need help with any part of this article, join our Discord Server where you can ask questions, discuss what you're working on, and I'll be more than happy to help.

You can find the complete source code for this project inside this GitHub repository.

👉 Checkout the other blog posts in this series:


This article contains materials adapted from "Chat app with React Native" by Aman Mittal, originally published at Heartbeat.Fritz.Ai.

This article features an image by Volodymyr Hryshchenko.