💆‍♂️

Amazon SNS と FCM を使用したプッシュ通知機能の構築(iOS/Android)とその注意点

2024/12/16に公開

Amazon SNS に FCM から取得してきた鍵情報を登録しておくことで、 FCM の API を使用したプッシュ通知機能を簡単に作成することができます。
FCM 関連はドキュメントの記載が曖昧な部分が多いのでハマりそうなポイントについて補足していきます。
<前提>

  • SNSプラットフォームアプリケーションを使用します
  • トピックは使用せず、個別へのプッシュ通知になります

アーキテクチャ

以下のようなアーキテクチャで作成しました。(通知送信イベントの発生など一部簡略化しています。)

利用登録フロー

1. 利用登録リクエスト

まずは、プッシュ通知自体の利用登録を行います。プッシュ通知を送る際、デバイスを識別するのはデバイストークンになります。アプリごとに異なるかと思いますが、端末ごとのIDとデバイストークンをリクエストとして送信します。

2. エンドポイント作成

次にSNS 上にエンドポイントを作成します。SNSエンドポイントは特定のユーザーに通知を送るための住所のようなものになります。SNSにデバイストークンを送信することで、そのデバイストークン(つまりユーザー)に対してのAWS上での住所が作成されます。

3. 利用者情報登録

DynamoDB に端末ID、デバイストークン、エンドポイントARNを格納します。
2 のステップでエンドポイントを作成したため、通知を送るためにはすでにデバイストークン自体は不要ですが、デバイストークンは不定期で洗い替えが行われるため保管が必要になります。
(※後述します。)

プッシュ通知送信フロー

4. 利用者情報取得

プッシュ通知を送る際、3で格納したエンドポイントARNを使用します。端末IDをキーに検索し、エンドポイントを取得します。

5. 通知履歴登録

プッシュ通知自体は100%到達が保証されるものではありません。そのため、リアルタイムで取得できなくても、履歴として確認ができるように保存をしています。
(AWS上で送信が失敗した時はフラグを立て再送するなどの拡張も行うことができます。)

6. 通知送信リクエスト

SNSに対してプッシュ通知送信リクエストを行います。前述したようにエンドポイントARNに対してリクエストを行います。今回はAWS SDK javascript v3のSNSClientを使用しました。
AndroidかiOSかでペイロードの形が違います。また、FCM上で特別な定義がないパラメータ(以下のカスタムパラメータA)を送信することもできます。
この辺りはドキュメントが曖昧なため、どの形式で送るかは検証をしながら定めました。(色々な形式で送ることができるようなのであくまで一例として参考にしてください。)

   // Android の場合
   const payloadAndroid = {
      data: {
        title: "title",
        body: "body",
        カスタムパラメータA: "カスタムパラメータA"
      },
    };

   // iOS の場合
   const payloadIOs = {
      notification: {
        title: "title",
        body: "body",
      },
      data: {
        カスタムパラメータA: "カスタムパラメータA"
      }
    };

    const payload = {
      GCM: JSON.stringify(payloadAndroid or ),
    };
    
    const params: PublishCommandInput = {
      MessageStructure: "json",
      Message: JSON.stringify(payload),
      TargetArn: endpointArn, // SNSのエンドポイント ARN
    };

    
    const response = await this.SNSClient.send(new PublishCommand(params));

https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-send-custom-platform-specific-payloads-mobile-devices.html

https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?hl=ja#ApnsConfig

🚨注意喚起

デバイストークンは定期的に洗い替えが必要です

https://firebase.google.com/docs/cloud-messaging/manage-tokens

  • 新しいデバイスにアプリが復元された場合
  • ユーザーがアプリをアンインストールまたは再インストールした場合
  • ユーザーがアプリのデータを消去した場合

など、さまざまなタイミングでデバイストークンは更新されますが、それ以外にもトークンが有効期限切れになることがあるようです。(あまり明言はされていなくわかりづらく、私もモバイルの有識者に聞くまで長いこと分からず結構揉めました)

そのため、定期的に最新のトークンを取得し、変更があった場合にはバックエンド側で更新をかける必要があります。(アプリを立ち上げた時にAPIコールをするなど)
更新は以下のドキュメントの疑似コードを参考に行なっています。このコードはエンドポイントの新規作成/更新どちらの場合にも対応した冪等性を持ったものになっています。

retrieve the latest device token from the mobile operating system
if (the platform endpoint ARN is not stored)
  # this is a first-time registration
  call create platform endpoint
  store the returned platform endpoint ARN
endif

call get endpoint attributes on the platform endpoint ARN 

if (while getting the attributes a not-found exception is thrown)
  # the platform endpoint was deleted 
  call create platform endpoint with the latest device token
  store the returned platform endpoint ARN
else 
  if (the device token in the endpoint does not match the latest one) or 
      (GetEndpointAttributes shows the endpoint as disabled)
    call set endpoint attributes to set the latest device token and then enable the platform endpoint
  endif
endif

https://docs.aws.amazon.com/ja_jp/sns/latest/dg/mobile-platform-endpoint.html

Discussion