Create your custom notification system with Socket.io and React Native

lynxgsm

AILI Fida Aliotti Christino

Posted on January 30, 2023

Create your custom notification system with Socket.io and React Native

As a mobile developer, i'm sure you came across this problem:

How can i implement notifications to my mobile app?

šŸ‘ØšŸ»ā€šŸ¦± I'm curious about your response, so please take a few minutes to comment on how you do it.

The most common way i heard to do this is to add firebase to your project and use Firebase Cloud Messaging (FCM). But, what if i don't want to use FCM?

Well let's find out!

The system itself

OK, this is how we're gonna do it: what if we have a react native app with a background service that will listen to socket events and displays notifications based on these events? Well, it sounds logic to me so let's implement this.

The implementation

Now that we've got the general idea, let's split this into parts:

  • First, we need to create a react-native app
  • Add background services and socket service to it
  • Create our backend socket server
  • Connect them

šŸ“¢ Before we start, i assume that you've correctly setup your android and iOS development environment. If not, pay a visit to the react-native configuration website.

1. React Native App

So, let's create our first application. For that, all we need to do is run this command:



yarn react-native init $yourappname --template react-native-template-typescript


Enter fullscreen mode Exit fullscreen mode

OK, let's break it down: it will copy the files for a basic TypeScript react-native app inside a folder called $yourappname (feel free to use your preferred package manager). In my case, i named my app: cassandra (don't ask me why šŸ˜…).

That's it! All we have to do is wait for this command to complete ā°. When this is done, navigate to your newly fresh created app then try to run it by launching this command:



yarn android


Enter fullscreen mode Exit fullscreen mode

And hopefully, šŸ¤ž it will run on your device without any errors.

2. Adding foreground services and socket client

Let's jump to the next part: installing our dependencies. For the foreground service, i will use the @supersami/rn-foreground-service package.

Let's try to add this:



yarn add @supersami/rn-foreground-service


Enter fullscreen mode Exit fullscreen mode

Then add this line to configure it in our app:



node node_modules/@supersami/rn-foreground-service/postinstall.js


Enter fullscreen mode Exit fullscreen mode

This line šŸ‘† will add the necessary permissions in our AndroidManifest file.

Now we're good šŸ˜Š!

After a quick documentation reading šŸ„±, we need to modify our index.js file:



import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
// Use the package
import ReactNativeForegroundService from '@supersami/rn-foreground-service';

// Add this line
ReactNativeForegroundService.register();
AppRegistry.registerComponent(appName, () => App);


Enter fullscreen mode Exit fullscreen mode

OK, let's leave it for the moment. Now, time for socket client to be installed.



yarn add socket.io-client


Enter fullscreen mode Exit fullscreen mode

Ok, we're good for now, we will use them later. Let's jump into the next topic šŸ‘‰

3. Creating our backend service

For our backend, i will use a simple Node project. Let's initialize it by typing mkdir backend && yarn init -y. It will create a folder named backend and initiate it with a package.json file (The flag -y is to tell yarn to answer "yes" to every question asked).

Now, let's add our packages: yarn add express socket.io. OK, we'll create two files index.js and index.html at the root of the project and copy these lines respectively:



// index.js file

// Import packages
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
// Initiate our server
const app = express();
const server = http.createServer(app);
const io = new Server(server);
const PORT = process.env.PORT || 4000;

// Check events
io.on("connection", (socket) => {
  socket.on("chat_message", (data) => {
    socket.broadcast.emit("show_notification", data);
  });
});

// Serve index.html file
app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

// Run our server
server.listen(PORT, () => {
  console.log("listening on port:", PORT);
});


Enter fullscreen mode Exit fullscreen mode

Barely explained, we will create a socket server that runs on the port 4000. The socket will listen to the event chat_message and will send a broadcast message with the data obtained. Now, let's write our index.html file:



<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      body {
        margin: 0;
        padding-bottom: 3rem;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Helvetica, Arial, sans-serif;
      }

      #form {
        background: rgba(0, 0, 0, 0.15);
        padding: 0.25rem;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        display: flex;
        height: 3rem;
        box-sizing: border-box;
        backdrop-filter: blur(10px);
      }
      .input {
        border: none;
        padding: 0 1rem;
        flex-grow: 1;
        border-radius: 2rem;
        margin: 0.25rem;
      }
      #input:focus {
        outline: none;
      }
      #form > button {
        background: #333;
        border: none;
        padding: 0 1rem;
        margin: 0.25rem;
        border-radius: 3px;
        outline: none;
        color: #fff;
      }

      #messages {
        list-style-type: none;
        margin: 0;
        padding: 0;
      }
      #messages > li {
        padding: 0.5rem 1rem;
      }
      #messages > li:nth-child(odd) {
        background: #efefef;
      }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input
        class="input"
        placeholder="User name"
        id="user"
        autocomplete="off"
      />
      <input
        class="input"
        id="input"
        placeholder="Message"
        autocomplete="off"
      /><button>Send</button>
    </form>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io();

      const form = document.getElementById("form");
      const input = document.getElementById("input");
      const user = document.getElementById("user");

      form.addEventListener("submit", function (e) {
        e.preventDefault();
        if (input.value && user.value) {
          socket.emit("chat_message", {
            message: input.value,
            user: user.value,
          });

          input.value = "";
        }
      });
    </script>
  </body>
</html>


Enter fullscreen mode Exit fullscreen mode

This is a slight ameliorated version of the official documentation on how to create a chat application with socket.io.

Now, you probably have an image in your head of what's going to be done here.

A brilliant mind šŸ§  like yours guessed that the user will send a message (event) from the web part, the server will listen to that same event, trigger a broadcast event to whichever client listening to the server event then do an action according to it šŸ˜. We're getting close now šŸ„µ

4. Connect them

Now let's go back to our react-native project and modify our App.jsx:



import {View, Text} from 'react-native';
import React, {useEffect, useState} from 'react';
import {io, Socket} from 'socket.io-client';
import ReactNativeForegroundService from '@supersami/rn-foreground-service';

// In my case, i used ngrok to forward my server to have an https one
const serverurl = *your server url*

const App = () => {
  const socket: Socket = io(serverurl);
  const [connected, setconnected] = useState(socket.connected);

  useEffect(() => {
    socket.on('connect', () => {
      setconnected(socket.connected);
      if (!ReactNativeForegroundService.is_running) {
        ReactNativeForegroundService.start({
          id: 80,
          title: 'Message from: ',
          message: "You're connected",
        });

        ReactNativeForegroundService.add_task(() => {}, {
          delay: 5000,
          onLoop: false,
          taskId: '80',
          onError: e => console.log('Error logging:', e),
        });
      }
    });

    socket.on('show_notification', data => {
      ReactNativeForegroundService.update({
        id: '80',
        message: data.message,
        title: `Message from ${data.user}`,
      });
    });

    return () => {
      ReactNativeForegroundService.stop();
    };
  }, [socket]);

  return (
    <View>
      <Text>{connected ? 'Connected' : 'Waiting for connection'}</Text>
    </View>
  );
};

export default App;


Enter fullscreen mode Exit fullscreen mode

Let's resume what we've done here:

  • Initialize the socket client with your server url
  • When connected to the server, we will initialize the foreground service if not done yet
  • We will add a new foreground service task
  • We will listen to the event from the server so then we can show a notification based on received data.

Now that the puzzle is complete, let's run everything.



// React native app
yarn android

Enter fullscreen mode Exit fullscreen mode


// Node.js app
node index.js

Enter fullscreen mode Exit fullscreen mode



  1. Wrap up & test

Now that everything is running, open your browser and go to localhost:4000 (as i set 4000 as my port number) and you should see this at the bottom of the page:

webpage

Try to write something some fancy message:

test

As soon as you hit enter, look what's displayed in your android app:

result

Try to close your app and try again. What happens? It still works! šŸ˜ŠšŸŽ‰ Now you have your own notification system.

You can even go further by hosting your backend for free on Render

Drawbacks

Yes, there are a few:

  • Having a constant notification tile to tell the user that our app is running on background
  • Queue system is not handled. Let me explain: let's suppose our user is not connected to the internet for some reasons. When he gets back, the previous events sent from the server will just pass through it without warning us that there were unread notifications because in case events aren't intercepted, they will be lost in nature šŸ„²
  • You need to have an https server

That's all folks! Thank you for reading this article, i hope you learnt something new or helped you in your project. More contents are coming!

šŸ’– šŸ’Ŗ šŸ™… šŸš©
lynxgsm
AILI Fida Aliotti Christino

Posted on January 30, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related