React Native - Push Notifications

I’m going to use Batch to make things easier.

Install

npm install --save react-native-push-notification
react-native link
pip install python-gcm apns

Client Side

Get the ID:

Android

https://developers.google.com/mobile/add

iOS

http://facebook.github.io/react-native/docs/integration-with-existing-apps.html#app-transport-security

sudo gem install fastlane
fastlane pem

Add the following to the client:

var PushNotification = require('react-native-push-notification');

// THIS PART GOES OUTSIDE ALL CLASSES !!!!!!!!!!
export function postForm(path, form) {
  const str = [];
  for (let p in form) {
    str.push(encodeURIComponent(p) + "=" + encodeURIComponent(form[p]));
  }
  const body = str.join("&");
  const req = {
    method: 'post',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body
  };
  return fetch(path, req);
}

// THIS PART GOES IN constructor(props) !!!!!!!!!!!
PushNotification.configure({
    // (optional) Called when Token is generated (iOS and Android)
    onRegister: function(t) {
        console.log(t)
        postForm('http://192.168.0.120:5020/', {token: t.token})
    },

    // (required) Called when a remote or local notification is opened or received
    onNotification: function(notification) {
        console.log( 'NOTIFICATION:', notification );
    },

    // ANDROID ONLY: GCM Sender ID (optional - not required for local notifications, but is need to receive remote push notifications)
    senderID: "",

    // IOS ONLY (optional): default: all - Permissions to register.
    permissions: {
        alert: true,
        badge: true,
        sound: true
    },

    // Should the initial notification be popped automatically
    // default: true
    popInitialNotification: true,

    /**
      * (optional) default: true
      * - Specified if permissions (ios) and token (android and ios) will requested or not,
      * - if not, you must call PushNotificationsHandler.requestPermissions() later
      */
    requestPermissions: true,
});

On Android, add the following to AndroidManifest.xml

<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission
    android:name="${applicationId}.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

And then in the application brackets:

<application ....>
    <receiver
        android:name="com.google.android.gms.gcm.GcmReceiver"
        android:exported="true"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="${applicationId}" />
        </intent-filter>
    </receiver>

    <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
    <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>
    <service
        android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>

Server Side

That’s it for the client side. Now for the server side:

from gcm import *
from time import sleep
#from apns import APNs, Frame, Payload
from izzati import Backend

gcm = GCM("AIzaSyBUXAxT9Nynnx6ZbQfjnZL9-Ep3d37wBxY")
#apns = APNs(use_sandbox=True, cert_file='cert.pem')

tokens = []

def callback(data, files):
    global gcm, tokens
    print(data)
    token = data['token']
    tokens.append(token)
    tokens = list(set(tokens))
    data = {'title': 'Test Notifications', 'message': 'Hello!'}
    #payload = Payload(alert="Hello World!", sound="default", badge=1, mutable_content=True)

    #apns.gateway_server.send_notification(token, payload)
    gcm.plaintext_request(registration_id=token, data=data)
    sleep(10)
    gcm.plaintext_request(registration_id=token, data=data)

b = Backend(callback)
b.run()

For more info on the data input:

https://developers.google.com/cloud-messaging/http-server-ref#notification-payload-support

That’s it!

Expo - Push Notifications

Install

Install Expo as you usually would.

This will only work on a non-ejected Expo project!!!

Install the Python server side.

sudo pip install exponent_server_sdk izzati

https://docs.expo.io/versions/v18.0.0/guides/push-notifications.html

Client Side

import Expo from 'expo';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Permissions, Notifications } from 'expo';

const PUSH_ENDPOINT = 'http://192.168.0.120:5020/';

export function postForm(path, form) {
  const str = [];
  for (let p in form) {
    str.push(encodeURIComponent(p) + "=" + encodeURIComponent(form[p]));
  }
  const body = str.join("&");
  const req = {
    method: 'post',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body
  };
  return fetch(path, req);
}

async function registerForPushNotificationsAsync() {
  const { existingStatus } = await Permissions.getAsync(Permissions.REMOTE_NOTIFICATIONS);
  let finalStatus = existingStatus;

  // only ask if permissions have not already been determined, because
  // iOS won't necessarily prompt the user a second time.
  if (existingStatus !== 'granted') {
    // Android remote notification permissions are granted during the app
    // install, so this will only ask on iOS
    const { status } = await Permissions.askAsync(Permissions.REMOTE_NOTIFICATIONS);
    finalStatus = status;
  }

  // Stop here if the user did not grant permissions
  if (finalStatus !== 'granted') {
    return;
  }

  // Get the token that uniquely identifies this device
  let t = await Notifications.getExponentPushTokenAsync();

  // POST the token to our backend so we can use it to send pushes from there
  return postForm(PUSH_ENDPOINT, {token: t})
}

class App extends React.Component {
    state = {
      notification: {},
    };

    componentWillMount() {
      registerForPushNotificationsAsync();

      // Handle notifications that are received or selected while the app
      // is open. If the app was closed and then opened by tapping the
      // notification (rather than just tapping the app icon to open it),
      // this function will fire on the next tick after the app starts
      // with the notification data.
      this._notificationSubscription = Notifications.addListener(this._handleNotification);
    }

    _handleNotification = (notification) => {
      this.setState({notification: notification});
    };

    render() {
      return (
        <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
          <Text>Origin: {this.state.notification.origin}</Text>
          <Text>Data: {JSON.stringify(this.state.notification.data)}</Text>
        </View>
      );
    }
}

The Server

from exponent_server_sdk import DeviceNotRegisteredError
from exponent_server_sdk import PushClient
from exponent_server_sdk import PushMessage
from exponent_server_sdk import PushResponseError
from exponent_server_sdk import PushServerError
from requests.exceptions import ConnectionError
from requests.exceptions import HTTPError

from izzati import Backend
from time import sleep


# Basic arguments. You should extend this function with the push features you
# want to use, or simply pass in a `PushMessage` object.
# https://docs.expo.io/versions/v18.0.0/guides/push-notifications.html#http-2-api
tokens = []

def send_push_message(token, message, extra=None):
    try:
        response = PushClient().publish(
            PushMessage(to=token,
                        body=message,
                        data=extra))
    except PushServerError as exc:
        # Encountered some likely formatting/validation error.
        raise
    except (ConnectionError, HTTPError) as exc:
        # Encountered some Connection or HTTP error - retry a few times in
        # case it is transient.
        raise self.retry(exc=exc)

    try:
        # We got a response back, but we don't know whether it's an error yet.
        # This call raises errors so we can handle them with normal exception
        # flows.
        response.validate_response()
    except DeviceNotRegisteredError:
        # Mark the push token as inactive
        from notifications.models import PushToken
        PushToken.objects.filter(token=token).update(active=False)
    except PushResponseError as exc:
        raise self.retry(exc=exc)

def register_token(data, files):
    global tokens
    print(data)
    tokens.append(data['token'])
    sleep(10)
    send_push_message(data['token'], "hello")


b = Backend(register_token)
b.run()