wheatandcatの開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

Expo + React NavigationでPush通知からのDeep Linkを実装

PUsh通知から指定の画面に遷移させる機能を作成したかったので実装

Pull Request

github.com

実装

まず、以下を参考にReact NavigationでDeep Linkを実装

https://reactnavigation.org/docs/deep-linking

src/Router.tsx

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import * as Notifications from 'expo-notifications';
import { createStackNavigator } from '@react-navigation/stack';
import * as Linking from 'expo-linking';
import Home, { HomeScreenOption } from 'components/pages/Home';
import theme from 'config/theme';

const Stack = createStackNavigator();
const prefix = Linking.createURL('/');

const WithProvider = () => {
  return (
    <NavigationContainer
      linking={{
        prefixes: [prefix],
        subscribe(listener) {
          const onReceiveURL = ({ url }: { url: string }) => {
            listener(url);
          };

          Linking.addEventListener('url', onReceiveURL);

          const subscription = Notifications.addNotificationResponseReceivedListener(
            (response) => {
              const url =
                response.notification.request.content.data?.urlScheme ?? '';

              if (url !== '') {
                listener(`${prefix}${url}`);
              }
            }
          );

          return () => {
            Linking.removeEventListener('url', onReceiveURL);
            subscription.remove();
          };
        },
      }}
    >
...略)

Expoはクライアントアプリで実行時とスタンドアローンアプリでschemaが変わってしまうので、expo-linkingを使用して指定する

const prefix = Linking.createURL('/')

今回は複雑なパラメータ設定が無いので設定していませんが、もしパラメータが必要な場合は、Configuring linksから設定可能です。

上記の実装が完了したら、以下のコマンドでDeep Linkが行えるか確認できます。

$ npx uri-scheme open exp://127.0.0.1:19000/--/MyPage --ios

MyPageはNavigationのScreen Nameになるので、実行してマイページ画面に遷移できれば正常に設定できています。

続いてPush通知からの遷移の実装です。以下が対象のコードです。(以下を参考に実装)

https://reactnavigation.org/docs/deep-linking/#third-party-integrations

subscribe(listener) {
  const onReceiveURL = ({ url }: { url: string }) => {
    listener(url);
  };

  Linking.addEventListener('url', onReceiveURL);

  const subscription = Notifications.addNotificationResponseReceivedListener(
    (response) => {
      const url =
        response.notification.request.content.data?.urlScheme ?? '';

      if (url !== '') {
        listener(`${prefix}${url}`);
      }
    }
  );

  return () => {
    Linking.removeEventListener('url', onReceiveURL);
    subscription.remove();
  };
},

Push通知のdataにurlSchemeのパラメータが設定している場合は、画面遷移するように実装されています。

      const url =
        response.notification.request.content.data?.urlScheme ?? '';

      if (url !== '') {
        listener(`${prefix}${url}`);
      }

これで実装は完了です。 最後にデバッグ用にローカル Push通知にurlSchemeのパラメータを設定して想定通りに動作しているか確認します。

src/components/organisms/Debug/Debug.tsx

import React, { memo, useCallback, useState } from 'react';
import { StyleSheet, TouchableOpacity, Alert } from 'react-native';
import * as Notifications from 'expo-notifications';

...略)

type Props = {};

const Menu: React.FC<Props> = () => {
  const onLocalPushNotification = useCallback(async () => {
    const {
      status: existingStatus,
    } = await Notifications.getPermissionsAsync();
    let finalStatus = existingStatus;

    if (existingStatus !== 'granted') {
      const { status } = await Notifications.requestPermissionsAsync();
      finalStatus = status;
    }

    if (finalStatus !== 'granted') {
      return false;
    }

    Notifications.scheduleNotificationAsync({
      content: {
        body: 'Push通知テスト',
        data: {
          urlScheme: 'MyPage',
        },
      },
      trigger: {
        seconds: 3,
      },
    });

    Alert.alert('3秒後に通知を設定しました');
  }, []);

  return (
      <TouchableOpacity onPress={onLocalPushNotification}>
        <View>
          <Text fontFamily="NotoSansJP-Bold">ローカルPush通知テスト</Text>
        </View>
      </TouchableOpacity>
...略)

});

実際に動作させると、以下のようになりました。

www.youtube.com