Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent socket.io from disconnecting when react native app is in background/device is lock?

As shown in title, my socket server will determine my react native app is 'dead' when my device is on lock screen or background.

What are the solution to these?

like image 765
Jin Tan Avatar asked Aug 16 '20 03:08

Jin Tan


People also ask

Is it possible to run a react native app in the background?

With the help of Headless JS, Native Modules, and WorkManager, it is fairly easy to create and run JavaScript tasks in React Native. In this article, we covered from beginning to end setting up and running Headless JS in React Native Android, allowing us to run tasks in the background of our application.

Can socket IO be used with react native?

The latest version of socket. io-client 2.2. 0 does not work with React-Native currently apparently, so you need to install and use version 2.1.

Does react native support WebSockets?

React Native also supports WebSockets, a protocol which provides full-duplex communication channels over a single TCP connection.


2 Answers

After doing some reading online, the reason my socket server will determine my react native app is 'dead' is because all of the javascript code inside my app has stopped working when my device is locked or in background.

Therefore, i have figured out a solution to solve this problem.

Tools i have used:

  1. React Native Appstate
  2. React Native Background Timer

My client side code:

import React, {useEffect, useRef} from 'react';
import {AppState} from 'react-native'

export default () => {
  const appState = useRef(AppState.currentState);
  var interval

  const _handleAppStateChange = (nextAppState) => {
      if (
        appState.current.match(/inactive|background/) &&
        nextAppState === "active"
      ) {
        console.log("App has come to the foreground!");
        //clearInterval when your app has come back to the foreground
        BackgroundTimer.clearInterval(interval)
        
      }else{
        //app goes to background
        console.log('app goes to background')
        //tell the server that your app is still online when your app detect that it goes to background
        interval = BackgroundTimer.setInterval(()=>{
          console.log('connection status ', socket.connected)
          socket.emit('online')
        },5000)
      appState.current = nextAppState;
      console.log("AppState", appState.current);
  }

useEffect (() => {
    AppState.addEventListener("change", _handleAppStateChange);

    return () => {
      AppState.removeEventListener("change", _handleAppStateChange);
    };
},[])



}


My Server side code:

io.on('connection',(socket)=>{
  socket.on('online',()=>{
    //do nothing
  })
}
)

This solution works on my app. Now socket will not disconnect until i close the app or click disconnect button.

like image 112
Jin Tan Avatar answered Oct 31 '22 09:10

Jin Tan


Although Jin Tan's answer worked for me and my socket connection remained stable, the process that I was using over the socket connection got shutdown (react-native-webrtc).

I used the ForegroundService Library to keep both the socket as well as the webrtc transport active. The combined code is as follows:

import VIForegroundService from '@voximplant/react-native-foreground-service';
import BackgroundTimer from 'react-native-background-timer';

  _handleAppStateChange = (nextAppState) => {
    if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
      console.log('App has come to the foreground!')
      // clearInterval when the app comes back to the foreground
      BackgroundTimer.clearInterval(interval)
    }else{
      // app goes to background
      console.log('app goes to background')
      // tell server that the app is still online when app goes to background
      interval = BackgroundTimer.setInterval(()=>{
        console.log('connection status ', socket.connected)
        this.socket.emit('online')
      },5000)
      this.setState({appState: nextAppState});
      console.log("AppState", this.state.appState);
    }
  }

  async configureForeground(){
    if (Platform.Version >= 26) {
      const channelConfig = {
        id: 'CallNotification',
        name: `Active Call Notification`,
        description: 'Active calls will be shown using this notification',
        enableVibration: false,
        importance: 1
      };
      await VIForegroundService.createNotificationChannel(channelConfig);
    }
    const notificationConfig = {
      id: 3456,
      title: 'title',
      text: 'text',
      icon: 'ic_notification',
      priority: 1
    };
    if (Platform.Version >= 26) {
      notificationConfig.channelId = 'voiceCallNotification';
    }
    await VIForegroundService.startService(notificationConfig);
  }

ic_notification is an image "ic_notification.png" in all the mipmap-xxxx folders.

You can call configureForeground() to start the notification. To end the foreground service in componentWillUnmount:

    AppState.removeEventListener('change', this._handleAppStateChange);
    try{
      VIForegroundService.stopService();
    }catch(err){
      // do nothing if service was never started.
    }
like image 24
Sarvesh Patil Avatar answered Oct 31 '22 09:10

Sarvesh Patil