LoginSignup
7
2

More than 5 years have passed since last update.

Push remote notification on Android with React Native

Last updated at Posted at 2018-06-08

Goal

  • Push remote notification
  • Show badge Unread notification

Development environment

We wil continue with the source code Github and development environment from previous article at link.

Note: if you haven't read previous article then please read it first.

Let's make

How to push notification ?

A push notification is a message the pops up on a mobile device. This message look like SMS text message and mobile alerts, but they only reach users who have installed your app. Each mobile platform has support for push notification, IOS, Android all have their own services. Through these services, App publishers can send notification the their users at any time.

Currently, there are many third-party services that enable push notification to both iOS and Android platforms such as:
1. FCM
2. SNS

In this article, I will instruct you to use the FCM service.

Flow control of FCM

Flow control FCM.png

  1. First, the client sends the token device to the server.
  2. Second, the server send notification to FCM service via the API with data includes message and device token of client.
  3. Third, FCM service push notification to client, and client show notification on screen.

Register account FCM and add new project.

If you have a google account, you can register account FCM easily in firebase console
Then, you must to add new project for your app.
Open your project and select Project settings. In tag General, you download file google-services.json file and place it in android/app directory.
Note: you can read guide at here

Next, you need find FCM server key API for backend server. It is in the cloud message tab.

FCM server key.png

Install package

In this article, I use 2 package for client react native and server rails
- Install react-native-fcm

npm install react-native-fcm --save
### in your Gemfile just include it: gem 'fcm'
bundle install

Implement Push remote notification

Android Configuration

You can reference here.

Implement client side

In Flow control of FCM, we can see that, client side has 2 function:
1. send device token for server side
2. listen event FCM push notification

Both of these tasks, react-native-fcm can support us with function
FCM.getFCMToken() and FCM.on(FCMEvent.Notification, async (notif) => {})
Create services folder and write code for FCM:

services/Fcm.js
import { Platform } from 'react-native';
import FCM, { FCMEvent } from "react-native-fcm";

export function saveTokenDevice() {
  FCM.requestPermissions({ badge: true, sound: true, alert: true });

  FCM.getFCMToken().then(token => {
    storage.save({
      key: 'device',
      data: {
        type: Platform.OS,
        token: token
      }
    });
  });
};

function showLocalNotification(title, body) {
  FCM.presentLocalNotification({
    id: 0,
    icon: "ic_launcher", // as FCM payload, you can relace this with custom icon you put in mipmap
    title: title,
    body: body, // as FCM payload (required)
    priority: "high",
    ongoing: false,
    wake_screen: true,
    show_in_foreground: true // notification when app is in foreground (local & remote)
  });
};

export function registerAppListener() {
  FCM.on(FCMEvent.Notification, async (notif) => {
    showLocalNotification(notif.title, notif.body);
  });
};
services/index.js
import * as Fcm from './Fcm';

export {
  Fcm
};
App.js
...
import { Fcm } from "./services";

...
Fcm.saveTokenDevice();
...

After save token device into storage when run app, I will send token device to backend server when client login.

components/Login.js
...
export default class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      id: '',
      password: '',
      isLogin: false,
      device: {},
    };
  }

  componentWillMount() {
    storage.load({
      key: 'device'
    }).then(ret => {
      this.setState({device: ret});
    })
  }

  authLogin() {
    axios.post(`http://ip_server:3001/auth/sign_in`, {
      email: this.state.id,
      password: this.state.password,
      device_type: this.state.device.type,
      device_token: this.state.device.token
      })
      .then(res => {
        this.setState({isLogin: true});
        this.sysnStorage(res);
        this.redirect_to_chat();
      })
      .catch(err => {
        alert(err);
      });
  }
  ...
}
...

Implement server side

To receive the token from the client, we will need the customer sign in

Create a Device model

rails g model device device_type device_token user:reference
rails db:migrate
config/routes.rb
Rails.application.routes.draw do
  mount_devise_token_auth_for 'User', at: 'auth', controllers: {
    sessions: 'devise_token_auth_extend/customer_sessions'
  }
  ...
devise_token_auth_extend/customer_sessions_controller.rb
module DeviseTokenAuthExtend
  class CustomerSessionsController < DeviseTokenAuth::SessionsController
    def create
      # Check
      field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first

      @resource = nil
      if field
        q_value = get_case_insensitive_field_from_resource_params(field)

        @resource = find_resource(field, q_value)
      end

      if @resource && valid_params?(field, q_value) && (!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
        valid_password = @resource.valid_password?(resource_params[:password])
        if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
         return render_create_error_bad_credentials
        end
        @client_id, @token = @resource.create_token
        @resource.save

        sign_in(:user, @resource, store: false, bypass: false)

        save_device

        yield @resource if block_given?

        render_create_success
      elsif @resource && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
        if @resource.respond_to?(:locked_at) && @resource.locked_at
          render_create_error_account_locked
        else
          render_create_error_not_confirmed
        end
      else
        render_create_error_bad_credentials
      end
    end

    private
    def save_device
      device = @resource.devices.find_or_initialize_by device_type: params[:device_type]
      device.update_attributes device_token: params[:device_token]
    end
  end
end
app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  ...
  def create messages
    messages["content"].each do |message|
      user_id = message["user"]["_id"]
      ChatMessage.create content: message["text"], user_id: user_id
      push_notification message["text"], Device.where.not(user_id: user_id).pluck(:device_token)
    end
  end

  private
  def push_notification message, device_tokens
    fcm = FCM.new ENV["fcm_server_key"], timeout: 3

    registration_ids = device_tokens # an array of one or more client registration tokens
    options = {
      data: {
        title: "You have a new message",
        body: message
      },
      collapse_key: "updated_score"
    }
    fcm.send registration_ids, options
  end
end

Note: ENV["fcm_server_key"] is taken from FCM server key on top.

Implement Show badge Unread notification

badge_notification.png

With react-native-fcm, we can implement function easily, only need use FCM.setBadgeNumber(number)

services/Fcm.js
...

export function registerAppListener() {
  FCM.on(FCMEvent.Notification, async (notif) => {
    FCM.getBadgeNumber().then(number => {
      showLocalNotification(notif.title, notif.body);
      FCM.setBadgeNumber(number + 1);
    });
  });
};

Note:

  • To display the badge, you must install on the real machine, because the emulator does not support badge.
  • In this article, I test on Xiaomi Redmi (Android 6) and Sony Xperia (Android 7), some other machine can not display because config permissions of that machine, you can search google how to config for different machine.

Final

This article is my basic research on React Native based on some article references on the internet. Application Chat may not complete, and has some errors. But I will continue implement Application Chat.
You can follow up on my source github

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2