Create your custom notification system with Socket.io and React Native
Tags: reactnative, socket, node, javascript
Published: 2023-01-30 19:10:33

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](https://reactnative.dev/) 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](https://reactnative.dev/docs/environment-setup).

###1. React Native App
So, let's create our first application. For that, all we need to do is run this command:
```bash
yarn react-native init $yourappname --template react-native-template-typescript
```

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:
```bash
yarn android
```

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](https://www.npmjs.com/package/@supersami/rn-foreground-service) package.

Let's try to add this:
```bash
yarn add @supersami/rn-foreground-service
```
Then add this line to configure it in our app:
```bash
node node_modules/@supersami/rn-foreground-service/postinstall.js
```

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:
```js
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);
```

OK, let's leave it for the moment. Now, time for socket client to be installed.
```bash
yarn add socket.io-client
```

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:
```js
// 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);
});
```

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:

```html
<!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>
```

This is a slight ameliorated version of the official documentation on [how to create a chat application with socket.io](https://socket.io/get-started/chat).

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**:
```tsx
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;
```

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.

```bash
// React native app
yarn android
```

```bash
// Node.js app
node index.js
```

###5. 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5svbu0o442745m475dj.png)

Try to write something some fancy message:

![test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h7ixr5fjouu3dqk3blym.png)

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


![result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vof38pvhf9laodvg4r07.jpg)

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](https://render.com/)

## 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!