Device to device push notification using Firebase

Ali Behboodian
6 min readJul 25, 2020

--

In this article, we will outline the different elements you need to be aware of to send a push notification from one device to another using Firebase. The architecture and mechanism described are common between iOS and Android. Our intention in this article is to give the reader an understanding of the architecture and how everything needs to come together to allow one device to send a push notification to another. While we keep this article at a high level, we refer the reader to other online resources to learn more about the details of each operation and be able to implement this feature.

A high level overview

Let’s look at the problem at a high level to identify different elements that are in play. Later in this article, we will delve into each element in more detail.

At a high level, the user of the sending device will take some action that will result in a change in the Firebase database. In return, Firebase will pick up this change and send a notification to another device, informing the receiving device about the change.

There are four main actions taken here:

  1. Updating of the database by the sending device
  2. Firebase listening for the change
  3. Firebase sending a notification to the receiving device(s) in reaction to the change
  4. Receiving device(s) presenting the notification to the user

In the rest of this article, we will go over each item above in more detail.

Updating of the database by the sending device

Updating Firebase database is well documented. If you are reading this article, you most likely understand how to setup your app to communicate with Firebase and write to the database. We will not go into any details here. You can find more about how to setup your device and communicate with the database here for iOS and here for Android.

Firebase listening for a change in the database

Here is where things get more interesting. The sending device writes to a path in the database and now you would like to inform another device about the change. The role of the sending device was simply to update the database and it ends there. What happens next is orchestrated by the Firebase Cloud Functions.

Cloud Functions for Firebase let you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your code for the functions is stored in Google’s cloud and runs in a managed environment. Here, we are interested in functions triggered by an event. And in this case, the event is a change in the database by the sending device.

In this architecture, there is no need to manage and scale your own server

A great reference to learn about Firebase functions is a YouTube video series by Doug Stevenson. There you also learn how to install required software and tools. Firebase functions are written in Java scripts or Type scripts. Note that regardless of if you are developing for Android or iOS, the Firebase functions will work the same.

A Firebase function has admin access to the database

Below is an example of a piece of Type script code that triggers when a new entry is created in a particular path in the database:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin'
admin.initializeApp();
export const onChatCreate = functions.database.ref('/Chats/{userId}/{chatId}')
.onCreate((snapshot, context) => {
const chatId = context.params.chatId
const data = snapshot.val();
...

In this case, the sending device creates a new entry in the path ‘/Chats/{userId}/{chatId}. Note the items in the curly brackets are place holders for parameters that the function does not have information about before it gets triggered. These parameters could be IDs automatically created by Firebase when writing a value to the database. However, once the function gets triggered, it has access to the values of these parameters as shown in the example above for the parameter chatId. The value written in the path is also accessible through the snapshot parameter as shown in the last line.

In the example above, we have used the onCreate method to trigger the function when an entry is created in the database at a particular path. You can also trigger this function when an entry gets deleted, updated, or written using the methods onDelete(), onUpdate(), and onWrite() respectively.

The line below will read another entry in the database once the function gets triggered using the method once():

return snapshot.ref.parent?.child(chatId).once("value").
then(snap => {
chat = snap.child('chats').child(subChatId).child('name').val()
...

The code snippets above are meant to give you a general idea of what you can do in a Firebase function. The YouTube video series mentioned above goes into more detail of how to write proper Type script code.

Firebase sending notification to receiving device(s)

The first question that comes to mind is how Firebase knows where to send a notification. Another question is how do you send the notification. We answer both of these questions in this section.

We use Firebase Cloud Messaging (FCM) to accomplish this task. FCM is a cross-platform messaging solution that lets you reliably send messages. You can learn more about FCM here. Make sure you go through the instructions to setup your iOS or Android applications.

Firebase provides an Instance ID for each app instance through the Firebase.InstanceId.FirebaseInstanceId class. Using this class, you can get a token that you should securely store in the Firebase database for each user of the app. This token is the key (think of it as an address) for Firebase to know where to send a notification.

Below is a function in JAVA for Android platforms to get the token for an app instance and save it in the database. Make sure you set the security rules of your database such that only the admin is able to read this token.

public void saveNewRegistrationTokenToServer() {
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
@Override
public void onComplete(@NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Toast.makeText(MainActivity.this, "getInstanceId failed", Toast.LENGTH_SHORT).show();
return;
}

// Get new Instance ID token
String token = task.getResult().getToken();
// Save the token in the database
}

The last line above saves the app instance token into a String. This string should be saved in the database in a location associated with the user of the app.

Finally note that the token might change if

  • The app is restored on a new device
  • The user uninstalls/reinstall the app
  • The user clears app data.

The second question to answer is how to send a notification. This is done through the same Firebase function that got triggered when the sending device updated a location in the database. The code snippet below shows how this is done in Type Script:

const payload = {
notification: {
title: 'Chat Complete!',
body: 'Chat "' + description + '" received'
}
return admin.messaging().sendToDevice(regTokens,payload)

Here the user can expect to see a message with the title “Chat Complete” and a text that depends on the variable description. There are more predefined keys available for building notification messages including color and image url for logos. These could be common across different platforms or could be specific for Android and iOS platforms. You can find links to different protocols here.

The last line in the code snippet above is responsible for sending the notification to the receiving device. Note that, regToken is the same token you saved in the database associated with the user you need to send the notification to. There should be a logic in the Type Script code that together with your app architecture, determines which user you should send the notification to. This completely depends on your application and its design. Also note that regTokens in the last line could be an array of tokens and the command will then send the same notification to more than one recipient.

Receiving notifications

At this point the receiving device will receive the notification only if the app is in the background. A notification will show up in the notification tray just like any other notification on the receiving device.

However, you may need to also handle and present the notification when the app is in the foreground. This is handled by a call back function in your app. To learn more about the details of receiving a notification when the app is in the foreground refer to this page for Android or this page for iOS.

This article tried to outline what is needed for a device to device notification using Firebase. Each section has significantly more details that is outside of the scope of this article. We encourage the reader who needs to implement device to device notification to refer to the references in this article and get more knowledgeable about the implementation details.

--

--