関連記事
Expoでつくったアプリにローカルサーバからプッシュ通知を送る 📛
Expoでつくったアプリにサーバからプッシュ通知を送る 📛
これまで、ローカルサーバからプッシュ通知送信、サーバからプッシュ通知送信を試しましたが、これまではデバイストークンを直書きしていました。
今回は、デバイストークンをCloud Firestoreに登録する部分をつくろうと思います。
注意事項
Expo(44.0.0)で開発した場合、 Firebase(9.6.8)を同時に使うと、「Can't find variable:IDBindex」というエラーが出るので、Firebase(9.6.7)を使って開発しました。
ソースコード
import Constants from 'expo-constants'; import * as Notifications from 'expo-notifications'; import { initializeApp } from 'firebase/app'; import { doc, getFirestore, setDoc } from 'firebase/firestore'; import { useState, useEffect, useRef } from 'react'; import { AppState, AppStateStatus, View } from 'react-native'; const firebaseConfig = { apiKey: 'XXXXXXXX', authDomain: 'XXXXXXXX.firebaseapp.com', projectId: 'XXXXXXXX', storageBucket: 'XXXXXXXX.appspot.com', messagingSenderId: 'XXXXXXXX', appId: 'XXXXXXXX' }; Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: true }) }); const app = initializeApp(firebaseConfig); export default function App() { const appState = useRef(AppState.currentState); const [ expoPushToken, setExpoPushToken ] = useState(''); useEffect(() => { AppState.addEventListener('change', handleAppStateChange); registerForPushNotificationsAsync().then((token) => { token && setExpoPushToken(token); }); return AppState.removeEventListener('change', handleAppStateChange); }, []); useEffect(() => { if (expoPushToken) { (async () => { const db = getFirestore(app); const ref = doc(db, 'tokens', expoPushToken); await setDoc(ref, { token: expoPushToken }); })(); } }, [expoPushToken]); function handleAppStateChange(nextAppState: AppStateStatus) { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { registerForPushNotificationsAsync().then((token) => { if (token) { setExpoPushToken(token); } }); } appState.current = nextAppState; } async function registerForPushNotificationsAsync() { let token; if (Constants.isDevice) { const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { return; } try { token = (await Notifications.getExpoPushTokenAsync()).data; } catch (err) { console.error(err); } } return token; } return ( <View />; ); }
ざっくり、こんな感じで実装しました。
ポイント
useEffect(() => { if (expoPushToken) { (async () => { const db = getFirestore(app); const ref = doc(db, 'tokens', expoPushToken); await setDoc(ref, { token: expoPushToken }); })(); } }, [expoPushToken]);
ここの部分でCloud Firestoreにデバイストークンを保存しています。
const ref = doc(db, 'tokens', expoPushToken);
これで、コレクションID「tokens」に対してデバイストークンをドキュメントIDにしたドキュメントを作成し、
await setDoc(ref, { token: expoPushToken });
「token」フィールドにデバイストークンを書き込んでいます。
ドキュメントIDにデバイストークンをつかい、デバイストークンの重複を防いでいるのですが、これがベストプラクティスなのかはわかりません。