React Native Tech Blog

supported by maricuru (旧maricuru tech blogです)

【React Native】【Expo】Expoでプッシュ通知を送る

React Native + Expoでプッシュ通知を送る方法をご紹介します。
公式で

it's almost too easy.

と書かれている通りで、実装はとても簡単です。

ちなみにExpoベースのアプリでプッシュ通知を送る場合、このExpo公式のプッシュ通知を使う以外方法はありません。
例えば、Firebase Cloud MessagingやAmazon Simple Notification Serviceなどのサービスは使えません。

(2018/5/2訂正) Expo SDK 27からFirebase Cloud Messagingがサポートされるようになりました。 Expo SDK v27.0.0 is now available – Exposition

プッシュ通知の実装

手順は大きく3つあります。
1. Expoのプッシュトークンを自分のサーバーに送る
2. Expoのプッシュ通知APIを叩く
3. 届いたプッシュ通知のハンドリング
といった感じです。

1. プッシュトークンを取得して保存

端末を特定するためのプッシュトークンを取得します。 このトークンはExpoが発行するもので、インストールする度に変わります。
トークンを取得したら、自分のサーバーで保存しておきます。

f:id:wasan:20180410200336p:plain

import { Permissions, Notifications } from 'expo';

const PUSH_ENDPOINT = 'https://your-server.com/users/push-token';

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

  // パーミッションがとれてないときだけ聞く
  if (existingStatus !== 'granted') {
    const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
    finalStatus = status;
  }

  // パーミッションを許可しなかった場合はreturn
  if (finalStatus !== 'granted') {
    return;
  }

  // Expoプッシュトークンを取得
  let token = await Notifications.getExpoPushTokenAsync();

  // 自分のサーバーにExpoプッシュトークンをPOSTする
  return fetch(PUSH_ENDPOINT, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      token: {
        value: token,
      },
      user: {
        username: 'Brent',
      },
    }),
  });
}

2. ExpoのプッシュAPIを叩く

先ほど取得したトークンと送りたいメッセージを、Expoのプッシュ通知APIにPOSTします。
iOSかAndroidかはExpoサーバー側で判断してくれます。なので開発者は、iOS/Androidを特に意識せずAPIを叩くだけでよいのは楽チンでいいですね。

f:id:wasan:20180410200748p:plain

具体的には、 https://exp.host/--/api/v2/push/send というエンドポイントにPOSTで、以下のようなJSONを送ります。

[{
  "to": "ExponentPushToken[xxxxxxxxxxxx]",
  "sound": "default",
  "body": "Hello world!"
}, {
  "to": "ExponentPushToken[yyyyyyyyyyyy]",
  "badge": 1,
  "body": "You've got mail"
}]

rubyだとこんな感じで実装できます。

        messages = [
          {
            to: 'ExponentPushToken[xxxxxxxxxxxx]',
            body: "hello",
          },
          {
            to: 'ExponentPushToken[yyyyyyyyyyyy]',
            body: "hello",
          },        
        ]
        end
        conn = Faraday.new(:url => 'https://exp.host/--/api/v2/push/send')
        conn.post do |req|
          req.headers['Content-Type'] = 'application/json'
          req.body = messages.to_json
        end
        response = conn.post '/push/send'  

3. 届いたプッシュ通知のハンドリング

iOSではアプリがフォアグラウンドにあると、通知欄にプッシュ通知は表示されません。
なのでアプリ側でハンドリングしてあげる必要があります。
よくあるのは、アプリ側でハンドリングして自前の通知バーを出す、などでしょうか。

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

// 先ほどの説明にあるregisterForPushNotificationsAsyncを参照
import registerForPushNotificationsAsync from './registerForPushNotificationsAsync';

export default class AppContainer extends React.Component {

  componentDidMount() {
    registerForPushNotificationsAsync();

    // アプリ起動中に受信、選択されたプッシュ通知をハンドリング
    this._notificationSubscription = Notifications.addListener(this._handleNotification);
  }

  _handleNotification = (notification) => {
    if(notification.origin === 'selected'){
      // バックグラウンドで起動中に通知がタップされたとき
    }else if(notification.origin === 'received'){
      // アプリ起動中に通知を受け取った
    }
  };

  render() {
    return (
      <View>
      </View>
    );
  }
}

補足:プッシュ通知の証明書は?

最後にここまで読んで、
「プッシュ通知の証明書とかはどうなってるの?」
と思われるかもしれません。

以前に書きましたが、EXPOではプッシュ通知の証明書もビルド時にEXPO側で管理してくれているので、開発者は意識する必要がありません!(これはとても楽♪)

以上になります。

以前のプロジェクトでは自前でAPNS、GCMを送信する仕組みを作っていましたが、それに比べると格段に楽になったと感じています。
是非お試しください。