みかづきブログ・カスタム

基本的にはちょちょいのほいです。

Cloud Firestoreに保存された値が変更された際にExpoで作ったアプリに通知を送信する 📛

f:id:kimizuka:20200615114604p:plain

Cloud Firestore トリガー を使って、ドキュメントに変更があった際にプッシュ通知を送ってみました。

firebase.google.com


前提

  1. Cloud Firestoreにデバイストークンを保存する(tokensコレクションにデバイスIDをドキュメントIDにして保存する)
  2. Cloud Firestoreに数字を保存する(notification/badge/currentにNumberを保存する)
  3. 保存された数字に変更があった際に通知を送ってバッジをつける
  4. iOSのみ対応(Androidがバッジ非対応のため)



ソースコード

const admin = require('firebase-admin');
const functions = require('firebase-functions');
const { Expo } = require('expo-server-sdk');

admin.apps.length ? admin.app() : admin.initializeApp();

exports.onUpdateBadge = functions.firestore.document('notification/badge').onUpdate(async (change) => {
  const tokens = await getTokens();
  const after = change.after.data();

  sendPushNotifications(tokens, after.current);
});

async function getTokens() {
  const db = admin.firestore();
  const query = db.collection('tokens').where('token', '>', '');
  const snapshot = await query.get();
  const tokens = snapshot.docs.map((doc) => {
    return doc.get('token');
  });

  return tokens;
}

async function sendPushNotifications(tokens = [], badge = 0) {
  let expo = new Expo({
    accessToken: process.env.ACCESS_TOKEN
  });

  const messages = [];

  tokens.forEach((token) => {
    messages.push({
      to: token,
      sound: 'default',
      title: '',
      badge,
      body: ''
    })
  });

  const chunks = expo.chunkPushNotifications(messages);
  const tickets = [];

  for (let chunk of chunks) {
    try {
      let ticketChunk = await expo.sendPushNotificationsAsync(chunk);

      tickets.push(...ticketChunk);
    } catch (error) {
      console.error(error);
    }
  }

  const receiptIds = [];

  for (let ticket of tickets) {
    if (ticket.id) {
      receiptIds.push(ticket.id);
    }
  }

  const receiptIdChunks = expo.chunkPushNotificationReceiptIds(receiptIds);

  for (let chunk of receiptIdChunks) {
    try {
      let receipts = await expo.getPushNotificationReceiptsAsync(chunk);

      for (let receiptId in receipts) {
        let { status, message, details } = receipts[receiptId];

        if (status === 'ok') {
          continue;
        } else if (status === 'error') {
          if (details && details.error) {
            console.error(message);
          }
        }
      }
    } catch (error) {
      console.error(error);
    }
  }
}



ポイント

// notification/badgeに変更があった際のコールバック
exports.onUpdateBadge = functions.firestore.document('notification/badge').onUpdate(async (change) => {
  const tokens = await getTokens();
  const after = change.after.data(); // 変更後の値を取得

  sendPushNotifications(tokens, after.current);
});

関数トリガーをつかって、すでに存在するドキュメントの値が変更されたときにトリガーを設定します。

firebase.google.com

sendNotificationの部分は、こちらの記事で解説しています。

blog.kimizuka.org



FirebaseのCloud Firestoreはコンソールから手動で書き換えることができるので、関数をデプロイすれば、手動で書き換えながデバッグすることができました。