4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React Native (Expo)で通知を送る

Last updated at Posted at 2022-04-11

二つの通知のタイプがあります:

  • ローカル通知:デバイスで設定されている
  • 外部通知:サーバーから来る

ローカル通知のユースケース:

  • 事前に設定することができる通知
  • リマインダー、締め切り等
  • 決済が完了した通知

外部通知のユースケース:

  • チャットメッセージが届く時
  • お知らせメッセージ

➤ このチュートリアルでは、ローカル通知も外部通知も送っていきます。手順は次のとおりです:

1. Expoプッシュトークンを取得する
2. アプリで通知の受け取りを設定する
3. テスト通知を送る

準備

Expoプロジェクトを作成しましょう。Visual Studio CodeなどのIDEでexpo init expo-push-notificationsを実行します。ターミナルで「blank template」を選びます。
必要なパッケージをインストールします: expo install expo-notifications expo-deviceを実行し、そうしたらcd expo-push-notificationsnpm startを実行します。 Expo開発環境の設定についてもっと知りたいなら、この記事をお読みください。

トークンの取得

App.jsxのコードをすべてを削除し、以下のコードに置き換えます。

App.jsx
import { Button, Platform, Text, View } from "react-native";
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import { useEffect, useState, useRef } from "react";

export default function App() {
 //プッシュトークンのバリュー
  const [expoPushToken, setExpoPushToken] = useState("");

 //プッシュ通知に登録する
  const registerForPushNofications = async () => {
    try {
   //アプリを実行しているデバイスはモバイルかどうかを確認する
      if (!Device.isDevice) {
        alert("このアプリはモバイルデバイスのみで実行できます");
        return;
      }
   //アプリは通知を受ける許可があるかどうかを確認する
      const { status: existingStatus } =
        await Notifications.getPermissionsAsync();
      let finalStatus = existingStatus;
   //まだ許可されていない場合には、許可を求める
      if (existingStatus !== "granted") {
        const { status } = await Notifications.requestPermissionsAsync();
        finalStatus = status;
      }
   //許可は否定されたら、ユーザに知らせる
      if (finalStatus !== "granted") {
        alert("プッシュ通知のトークンの取得ができませんでした。");
        return;
      }
    
   //トークンを取得して、スマホの画面で表示する
      const token = (await Notifications.getExpoPushTokenAsync()).data;
      console.log("token", token);
      setExpoPushToken(token);

      //アンドロイドなら、通知のオプションを変更することができます。
      //vibrationPatternとlightColorを変更してみてください!
      if (Platform.OS === "android") {
        Notifications.setNotificationChannelAsync("default", {
          name: "default",
          importance: Notifications.AndroidImportance.MAX,
          vibrationPattern: [0, 250, 250, 250],
          lightColor: "#FF231F7C",
        });
      }
    } catch {
      (err) => alert(err);
    }
  };

  useEffect(() => {
    registerForPushNofications();
    };
  }, []);

  return (
    <View
      style={{
        flex: 1,
        alignItems: "center",
        justifyContent: "space-around",
      }}
    >
      <Text>あなたのExpoプッシュトークン: {expoPushToken}</Text>
    </View>
  );
}

デバッグ

Notifications.getPermissionsAsync() is not a function

初めてコードを実行した時、expo-notificationsがインストールしていたのに、モバイルでNotificationsオブジェクトは空でありました。
同じエラーが発生すれば、次の手順に従って下さい:

1. 開発中のアプリとExpo Goアプリを閉じて、IDEのターミナルでサーバーを切って、もう一回npm startを実行します。
2.問題が続ければ、npm uninstall expo-notifications,npm i expo-notificationsを実行します。
3. 問題が続ければ、プロジェクトを削除して、再開します。新しいプロジェクトでnpm startを実行する前に、スマホで、前のアプリとExpo Goアプリを閉じます。

console.logが機能しません

Expoで, コードの変更はスマホの画面で表示されるまで、時々何回ものセーブが必要です。時々、UIの変更が表示されるのに、console.logやalertが機能しません。この問題が発生すれば、次の手順に従って下さい:

1. 開発中のアプリとExpo Goアプリを閉じて、もう一回開く
2. サーバーを切って、npm startをもう一度実行する
3. スマホの電源を切って、もうー同オンにする

通知受信の設定

アプリが通知を受け取る時の応答を設定しましょう。App.jsxに以下のコード追加します。

App.jsx
//ハンドラーを設定する
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    //通知を受け取る時に音が鳴らさないようにしたいなら、
    //shouldPlaySoundをfalseにして下さい。
    shouldPlaySound: false,
    shouldSetBadge: false,
  }),
});

export default function App() {
  ...
 //受け取った通知
  const [notification, setNotification] = useState('');
  const notificationListener = useRef();
  const responseListener = useRef();

  const registerForPushNofications = async () => {...};

  useEffect(() => {
    registerForPushNofications();
    
    //notificationを受け取った通知に設定する
    notificationListener.current =
      Notifications.addNotificationReceivedListener((notification) => {
        setNotification(notification);
      });
    
    //アプリがバックグラウンドにあり、ユーザーが通知をクリックした場合の対処方法
    responseListener.current =
      Notifications.addNotificationResponseReceivedListener((response) => {
        setNotification(response.notification);
        console.log(response.notification);
      });

    return () => {
      Notifications.removeNotificationSubscription(
        notificationListener.current
      );
      Notifications.removeNotificationSubscription(responseListener.current);
    };
  }, []);

  return (
    <View style={{...}}
    >
      <Text>あなたのExpoプッシュトークン: {expoPushToken}</Text>

      {/* 通知のタイトル、内容とデータを表示する */}
      <View style={{ alignItems: "center", justifyContent: "center" }}>
        <Text>
          タイトル: {notification && notification.request.content.title}{" "}
        </Text>
        <Text>内容: {notification && notification.request.content.body}</Text>
        <Text>
          データ:{" "}
          {notification && JSON.stringify(notification.request.content.data)}
        </Text>
      </View>
    </View>
  );
}

テスト通知の送信

ローカル通知

通知を送る関数と「送信」ボタンをApp.jsxに追加します。

App.jsx
...
  //ローカル通知お送る
  async function schedulePushNotification() {
    await Notifications.scheduleNotificationAsync({
      content: {
        title: "通知のタイトル! 📬",
        body: "通知の内容",
        data: { data: "通知のデータ" },
      },
      //通知が送信するまでに、何秒が掛かる
      trigger: { seconds: 2 },
    });
  }

  const registerForPushNofications = async () => {...};

...
      <Button
        title="通知を送信する"
        onPress={async () => {
          await schedulePushNotification();
        }}
      />
...

ボタンを押すと、通知が受け取ります!

外部通知

ExpoはPush notifications toolを提供します。

Capture d’écran (149)_LI.jpg

1.アプリを実行する時にIDEのターミナルで、token <あなたのトーケン>がログされています。そのトーケンをコピーして、ここでペストします
2.通知のタイトル(何でもいいです)
3.通知の内容(何でもいいです))
4.通知のデータ
5.通知が送信するまで、何秒が掛かる

それ以外の枠は入力しなくても大丈夫です。最後に「Send a Notification」ボタンをクリックして、スマホが通知を受け取ります。
Capture d’écran (150).png

まとめ

ローカル通知も外部通知も受信できるアプリを作りました!

完成したApp.jsx:

App.jsx
import { Button, Platform, Text, View } from "react-native";
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import { useEffect, useState, useRef } from "react";

//ハンドラーを設定する
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    //通知を受け取る時に音が鳴らさないようにしたいなら、
    //shouldPlaySoundをfalseにして下さい。
    shouldPlaySound: true,
    shouldSetBadge: false,
  }),
});

export default function App() {
  //プッシュトークンのバリュー
  const [expoPushToken, setExpoPushToken] = useState("");
  //受け取った通知
  const [notification, setNotification] = useState("");
  const notificationListener = useRef();
  const responseListener = useRef();

  //ローカル通知お送る
  async function schedulePushNotification() {
    await Notifications.scheduleNotificationAsync({
      content: {
        title: "通知のタイトル! 📬",
        body: "通知の内容",
        data: { data: "通知のデータ" },
      },
      //通知が送信するまで、何秒が掛かる
      trigger: { seconds: 2 },
    });
  }

  //プッシュ通知に登録する
  const registerForPushNofications = async () => {
    try {
      //アプリを実行しているデバイスはモバイルかどうかを確認する
      if (!Device.isDevice) {
        alert("このアプリはモバイルデバイスのみで実行できます");
        return;
      }

      //アプリは通知を受ける許可があるかどうかを確認する
      const { status: existingStatus } =
        await Notifications.getPermissionsAsync();
      let finalStatus = existingStatus;
      //まだ許可されていない場合には、許可を求める
      if (existingStatus !== "granted") {
        const { status } = await Notifications.requestPermissionsAsync();
        finalStatus = status;
      }
      //許可は否定されたら、ユーザに知らせる
      if (finalStatus !== "granted") {
        alert("プッシュ通知のトークンの取得ができませんでした。");
        return;
      }

      //トークンを取得して、スマホの画面で表示する
      const token = (await Notifications.getExpoPushTokenAsync()).data;
      console.log("token", token);
      setExpoPushToken(token);

      //アンドロイドなら、通知のオプションを変更することができます。
      //vibrationPatternとlightColorを変更してみてください!
      if (Platform.OS === "android") {
        Notifications.setNotificationChannelAsync("default", {
          name: "default",
          importance: Notifications.AndroidImportance.MAX,
          vibrationPattern: [0, 250, 250, 250],
          lightColor: "#FF231F7C",
        });
      }
    } catch {
      (err) => alert(err);
    }
  };

  useEffect(() => {
    registerForPushNofications();

    //notificationを受け取った通知に設定する
    notificationListener.current =
      Notifications.addNotificationReceivedListener((notification) => {
        setNotification(notification);
      });

    //受け取ったデータをログする
    responseListener.current =
      Notifications.addNotificationResponseReceivedListener((response) => {
        console.log(response);
      });

    return () => {
      Notifications.removeNotificationSubscription(
        notificationListener.current
      );
      Notifications.removeNotificationSubscription(responseListener.current);
    };
  }, []);

  return (
    <View
      style={{
        flex: 1,
        alignItems: "center",
        justifyContent: "space-around",
      }}
    >
      <Text>あなたのExpoプッシュトークン: {expoPushToken}</Text>

      {/* 通知のタイトル、内容とデータを表示する */}
      <View style={{ alignItems: "center", justifyContent: "center" }}>
        <Text>
          タイトル: {notification && notification.request.content.title}{" "}
        </Text>
        <Text>内容: {notification && notification.request.content.body}</Text>
        <Text>
          データ:{" "}
          {notification && JSON.stringify(notification.request.content.data)}
        </Text>
      </View>
      <Button
        title="通知を送信する"
        onPress={async () => {
          await schedulePushNotification();
        }}
      />
    </View>
  );
}

参考 

Firebase Cloud Messaging (アンドロイド)

Expo Goアプリを使わずにアンドロイドアプリを実行したいなら、Firebase Cloud Messagingが必要です。 アプリをApp Storeにアップロードする場合は、この点に注意してください。Firebase Cloud Messagingの設定方法: ドキュメンテーション (英語) , 記事(日本語)

バックエンド

ローカル通知のみを使用する場合を除いて、通知の送信ができるバックエンドを設定するのが必要です。そのバックエンドは、下のように、Expo Notifications APIにPOSTリクエストができる必要があります。

fetch('https://exp.host/--/api/v2/push/send', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Accept-Encoding': 'gzip, deflate',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: <Expoトーケン>,
    data: { data: 'データ' },
    title: 'タイトル',
    body: '内容',
  }),
})

これはこのチュートリアルの範囲外ですが、いくつかのオプションがあります:

  • One Signal
  • Next.jsでサーバーを作る (この記事を参考して下さい)
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?