React Nativeとプッシュ通知 (iOS編)

前回の記事Androidに対応したので、今回はiOSの対応を行なっていきます。

iOSのセットアップ

Xcode上でプロジェクトの設定を変更したいので /ios/RNPushNotification.xcworkspace を開きます。

最初に、前回にも設定したようにBundle Identifierが com.example.rn-push-notification.ios となっていることを確認します。 さらに通知を受信するためにCapabilityの中からBackground ModesPush Notifications を追加しておきます。 また証明書もまだであれば設定しておきましょう。

f:id:gibachan03:20200811081827p:plain:w400

Firebaseプロジェクトの設定

FirebaseコンソールでプロジェクトにiOSアプリを追加します。 ここで先ほど設定していたバンドルIDを指定しておきます。

f:id:gibachan03:20200811054136p:plain

続けてGoogleService-Info.plistをダウンロードしておき、残りの作業はスキップして大丈夫です。

そして次にプロジェクトにプッシュ通知を送るためのAPNs 認証キーを設定します。 まず証明書を作成するためにApple Developerへ行き、新たなKeyを作成してApple Push Notifications service (APNs)を有効にします。

f:id:gibachan03:20200811054651p:plain:w400

Keyをダウンロードし、画面に表示されているTeam IDとKey IDをメモしておきます。

Firebaseプロジェクトの設定からクラウドメッセージングに入ります。

f:id:gibachan03:20200811055015p:plain:w300

先ほど作成したKeyの情報を設定します。

f:id:gibachan03:20200811055238p:plain:w300

Firebaseの初期化

Xcodeの作業に戻ります。(ここの作業はドキュメントに沿っています)

もし前回実行していなければ下記コマンドでPodsを更新します。 cd ios/ && pod install

そして先ほどダウンロードしたGoogleService-Info.plistをプロジェクトに追加します。

f:id:gibachan03:20200811054451p:plain

Firebaseを初期化するコードを追加します。 AppDelegate.mを開き、ファイル先頭付近に下記コードを追加し、

#import <Firebase.h>

同じファイルのdidFinishLaunchingWithOptionsメソッドの中に下記コードを追加します。

if ([FIRApp defaultApp] == nil) {
  [FIRApp configure];
}

プッシュ通知の受信を許可

React Nativeの世界に戻ります。 iOSでは予め通知を受信する許可を得る必要があるため、その処理を追加します。

App.tsxを開き下記のようにコードを編集します

const App = () => {
  ...
  // 通知の許可をリクエストする
  async function requestUserPermission() {
    const authStatus = await messaging().requestPermission();
    const enabled =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL;

    if (enabled) {
      console.log('通知が許可されました');
    }
  }

  useEffect(() => {
    // 最初に通知の許可をリクエストする
    requestUserPermission();

    ...
  }, []);

  ...
};

ここまででプッシュ通知を受信する最低限の準備ができました。

プッシュ通知を送信

iOSシミュレータでは通知を受信できないので、Xcodeから実機でアプリを実行します。

最初に通知許可のダイアログが表示されるので許可し、アプリをバックグラウンドに入れます。

f:id:gibachan03:20200811060725p:plain:w300

前回と同様にFirebaseコンソールから通知を送信します。

f:id:gibachan03:20200811082026j:plain:w300

しかし、通知は受信できるかと思いますがAndroidで表示されてたような画像が表示されていないことがわかります。

iOSでは通知に画像を表示するにはNotification Service Extensionを実装する必要があります。 (ドキュメント: Modifying Content in Newly Delivered Notifications

Notification Service Extension

Notification Service Extension実装方法なのですが、React Nativeであっても特別なことはなさそうで、通常のネイティブ実装と同じ方法で実装することになるようです。つまり、/ios/RNPushNotification.xcworkspaceをXcodeで開いてSwift/Obj-Cを書いていくことになります。

実装についてはReact Nativeとも関係なく一般的なものになり、長くなってしまうので省略します。 (詳細はこちらのドキュメント

また、この場合の通知の送信方法も変わります。 プッシュ通知を受信した時にNotification Service Extensionを起動するためには、通知のペイロード“mutable-content” : 1 を含める必要があります。 (上記のドキュメント参照) そしてFirebaseコンソールからはこの設定が現在ではできないようです。 従って別の方法でプッシュ通知を送ります。

ここではレガシーなFCM HTTP APIを使って通知を送ってみます。 このAPIは手軽に使えて便利なのですが、実際にはよりセキュアなHTTP v1 APIを利用するのが良さそうです。

まず、APIの実行に必要なサーバーキーと送信者IDをFirebaseコンソールのSettings > クラウドメッセージングから確認します。

f:id:gibachan03:20200813081938p:plain:w300

コンソールからcurlを使ってAPIを実行します。

curl -X POST -H "Authorization: key=ここにサーバーキー" -H "project_id: key=ここに送信者ID" -H "Content-Type: application/json" -d '{
"to": "/topics/weather",
"notification": {
    "title": "通知テスト",
    "body": "Hello world",
    "image": "https://picsum.photos/id/1015/300/200" },
"mutable_content": true,
"data": {
    "imageURL": "https://picsum.photos/id/1015/300/200" }
}' https://fcm.googleapis.com/fcm/send

mutable_content を設定しておき、data.imageURL の画像をNotification Service Extensionで表示させます。 これで画像が表示されるはずです。

f:id:gibachan03:20200815094934p:plain:w200

以上です。

まとめ

今回この辺を調べたのはReact NativeでiOSのApp Extension(Notification Service Extensionなど)を実装するのはどうやるんだろう?との疑問が発端でした。結局それは一般的なネイティブ実装でやるっぽく、ちょっと残念でした。。。JS/TSで書ければ良かったな。

コードはここに置いておきます。 github.com