たかぎとねこの忘備録

プログラミングに関する忘備録を自分用に残しときます。マサカリ怖い。

React Native製のアプリで受け取ったプッシュ通知をタップして特定の画面に遷移させる方法

目次

  • FirebaseコンソールからServer Keyを取得する
  • Server KeyをExpoのサーバーにアップロードする
  • 必要なパッケージをインストールする

FirebaseコンソールからServer Keyを取得する

Expoが認証情報を使って開発者の代わりにExpoのサーバーからプッシュ通知を送信するためには、Server KeyをExpoにアップロードする必要がある。

このキーはFirebaseコンソールから確認することができる。

Firebase プロジェクトの設定ページにアクセスして、Cloud Messagingタブをクリックする。

Server KeyはCloud Messaging API (Legacy)でのみ利用可能なので、デフォルトでは無効になっています。なので3点メニューをクリックして、Google Cloud ConsoleでAPIを管理をクリックします。

Google CloudコンソールのCloud Messagingの詳細ページが開かれたら、有効にするボタンをクリックする。

有効になったら、自動的に次のようなページに遷移する。

Cloud Messagingタブのページに戻ったらCloud Messaging API有効になっているのが確認できる。その下にサーバーキーが表示されているのでコピーして控えておく。

Server KeyをExpoのサーバーにアップロードする

先ほど控えておいたServer KeyをExpoのサーバーにアップロードする。<your-token-here>の部分を実際のトークンに置き換えて次のコマンドを実行する。

expo push:android:upload --api-key <your-token-here>

これでアプリをインストールしているユーザーは、プロジェクトの認証情報を使用してFCMから通知を受け取ることができるようになる。

必要なパッケージをインストールする

プッシュ通知に対するハンドリングを行うのに必要なパッケージは次の2つ。

  • expo-device
  • expo-notifications
expo install expo-device expo-notifications

プッシュ通知の送信に必要なトークンを取得するための関数を実装する

プッシュ通知の送信にはメッセージを送信する宛先(Expo Push Token)の情報が必要になる。

そのトークンは実機上でしか取得できないので、クライアント上でgetPermissionsAsyncrequestPermissionsAsyncを呼び出して権限の可否を確認して、getExpoPushTokenAsyncを使って実際のトークンを取得する。

import * as Device from "expo-device";
import { Platform } from "react-native";
import * as Notifications from "expo-notifications";

export async function registerForPushNotifications() {
  let token = "";
  // 端末上でこの関数が実行されているかを確認する
  if (Device.isDevice) {
    // 通知の権限の状態を取得する
    const { status: existingStatus } =
      await Notifications.getPermissionsAsync();
    let finalStatus = existingStatus;
    // 通知が拒否されている場合
    if (existingStatus !== "granted") {
      // アラートを表示して、通知の許可を取得する
      const { status } = await Notifications.requestPermissionsAsync();
      finalStatus = status;
    }
    // 結局通知を拒否された場合
    if (finalStatus !== "granted") {
      alert("Failed to get push token for push notification!");
      return;
    }
    token = (await Notifications.getExpoPushTokenAsync()).data;
  } else {
    alert("Must use physical device for Push Notifications");
  }

  if (Platform.OS === "android") {
    Notifications.setNotificationChannelAsync("default", {
      name: "default",
      importance: Notifications.AndroidImportance.MAX,
      vibrationPattern: [0, 250, 250, 250],
      lightColor: "#FF231F7C",
    });
  }

  return token;
}

OSがAndroidの場合は、setNotificationChannelAsyncメソッドを呼び出して、第一引数で指定したチャンネルIDにチャンネル構成を割り当てる。 こにれより、サーバー側でExpoPushMessage型のオブジェクトを組み立てる時に、channelIdにここで指定したチャンネルIDを指定すると、ここで設定したチャンネル構成で通知が届くようになる。

トークンの取得に成功したら、任意のタイミングでサーバーに送信して保存するようにしてみる。

// screens/SetupWizard/NotificationSetupScreen.tsx
...
const expoPushTokenMutation = useFunctionsCall<
  UpdateConfigWithExpoPushTokenRequestData,
  UpdateConfigWithExpoPushTokenResponseData
>(functions, "updateConfigWithExpoPushToken");
...
// 通知権限の許可とExpo Push Tokenを取得する
const token = await registerForPushNotifications();
// トークンをexpo_push_tokensカラムの配列に追加する
if (token) {
  expoPushTokenMutation.mutate({
    expo_push_token: token,
  });
}
...

通知がタップされた場合の挙動を定義するためのフックを実装する

expo-notificationsから公開されているuseLastNotificationResponseフックを使うことで、直近でタップした通知の情報をレスポンスとして取得することができる。 この内容をuseEffectで処理することで、アプリがフォアグラウンドだろうと、閉じていた状態から通知をタップしてアプリが起動した状態だろうと、そのタップした通知のdataから求められている遷移先をurlとして取得することができる。

// hooks/useExpoNotifications.ts
import * as Notifications from "expo-notifications";
import { useEffect } from "react";
import * as Linking from "expo-linking";

export function useExpoNotifications() {
  const lastNotificationResponse = Notifications.useLastNotificationResponse();
  useEffect(() => {
    // data(JSON)の例
    // { "url": "scheme://path/into/app" }
    if (
      lastNotificationResponse &&
      lastNotificationResponse.actionIdentifier ===
        Notifications.DEFAULT_ACTION_IDENTIFIER
    ) {
      if (
        lastNotificationResponse.notification.request.content.data.url &&
        typeof lastNotificationResponse.notification.request.content.data
          .url === "string"
      ) {
        Linking.openURL(
          lastNotificationResponse.notification.request.content.data.url
        );
      }
    }
  }, [lastNotificationResponse]);

  return {
    response: lastNotificationResponse,
  };
}

あとはこのフックをRootNavigatorなどで呼び出すと良い。すると通知がタップされれば任意のアクションを実行することができる。 ここでは特定の画面に遷移するようにしている。

// navigation/index.tsx
...
const Stack = createNativeStackNavigator<RootStackParamList>();
...
function RootNavigator() {
  ...
  const { response } = useExpoNotifications();
  ...
  return (
    <Stack.Navigator>
    ...
    </Stack.Navigator>
  )
}

まとめ

今回の記事は前回公開したこちらの記事と一緒に読んでいただけるとより理解が深まるのではと思います。

takagimeow.hatenablog.com

expo-notificationsの詳細についてはこちらの公式ドキュメントを参照していただけると助かります。

Notifications - Expo Documentation