Hai dịch vụ native cần biết:
- APNs (Apple Push Notification service): iOS push.
- FCM (Firebase Cloud Messaging): Android push, có thể proxy iOS qua APNs.
Option 1 — Direct (@react-native-firebase/messaging + APNs):
- Setup phức tạp: cần Firebase project, push cert/auth key APNs, token registration.
- Mạnh: full control, không lock vào Expo, miễn phí (chỉ trả cho FCM enterprise).
- Code:
import messaging from '@react-native-firebase/messaging'
const token = await messaging().getToken() // gửi lên backend
messaging().onMessage(async (msg) => console.log('foreground', msg))
messaging().setBackgroundMessageHandler(async (msg) => console.log('background', msg))Option 2 — Expo Notifications (recommend cho Expo project):
- Wrapper hoàn chỉnh quanh APNs + FCM.
- API thống nhất, không cần care platform khác biệt.
- Có thể dùng Expo push service (FCM/APNs proxy) miễn phí, hoặc dùng FCM/APNs trực tiếp.
import * as Notifications from 'expo-notifications'
const { status } = await Notifications.requestPermissionsAsync()
const token = (await Notifications.getExpoPushTokenAsync()).data
// Gửi token lên backend, backend gọi Expo push API hoặc FCM
Notifications.addNotificationReceivedListener((notification) => {...})
Notifications.addNotificationResponseReceivedListener((response) => {
// user tap notification → navigate vào screen
})Backend send (Expo example):
await fetch('https://exp.host/--/api/v2/push/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
to: expoPushToken,
title: 'New message',
body: 'Hello!',
data: { screen: 'Chat', chatId: 42 },
}),
})Pitfall:
- iOS: cần aps-environment entitlement (development cho dev build, production cho TestFlight/App Store).
- Android 13+: cần permission POST_NOTIFICATIONS runtime.
- Foreground vs background handler khác nhau — test cả hai trạng thái.
- Token có thể expire (user reinstall app) — backend phải handle invalid token error gracefully.