(Mirrored from docs/guide/NOTIFICATION_SETUP_GUIDE.md — do not edit here; update the canonical file and re-sync.)
Firebase Cloud Messaging + Notifee Implementation Playbook
This document captures everything required to run the Yosemite Crew mobile push-notification stack end-to-end today, and what your backend teammates will need when the real service goes live.
Quick Reference
- Libraries:
@react-native-firebase/app@23.5.0,@react-native-firebase/messaging@23.5.0,@notifee/react-native@9.1.8 - Android channel id:
yc_general_notifications - Deep-link scheme:
yc:// - Key entry points
- JS bootstrap:
apps/mobileAppYC/App.tsx(<NotificationBootstrap />) - Service implementation:
apps/mobileAppYC/src/shared/services/firebaseNotifications.ts - Background handlers:
apps/mobileAppYC/index.js - Android manifest updates:
apps/mobileAppYC/android/app/src/main/AndroidManifest.xml - iOS AppDelegate:
apps/mobileAppYC/ios/mobileAppYC/AppDelegate.swift
- JS bootstrap:
Run after pulling changes:
pnpm install
cd apps/mobileAppYC/ios && pod install && cd ..
1. Mobile App Configuration
1.1 Shared prerequisites
- Firebase project must already contain the iOS and Android apps (this project already uses Firebase for OAuth).
- Ensure Cloud Messaging API (Legacy & HTTP v1) is enabled in the Firebase console.
- Confirm the existing
google-services.json(Android) andGoogleService-Info.plist(iOS) files are up to date. - Collect the FCM server key (for legacy API) and create a service account with the
Firebase Adminrole for HTTP v1 calls.
1.2 JavaScript bootstrap
-
initializeNotificationshandles:- Permission requests (API 33+ on Android + iOS alerts/badge/sound).
- Channel creation (Android).
- Background handlers (via Notifee + Firebase).
- Foreground presentation + Redux feed updates.
- Token management through the
onTokenUpdatecallback.
-
The bootstrap wrapper (
NotificationBootstrap) sits insideApp.tsx, so initialization happens exactly once after the Redux store is ready. Navigation intents are queued until theNavigationContainermounts. -
Background logic lives outside the component tree in
index.js:messaging().setBackgroundMessageHandler(handleBackgroundRemoteMessage); notifee.onBackgroundEvent(handleNotificationBackgroundEvent);
1.3 Deep linking contract
- New scheme
yc://is registered for both Android and iOS so links such asyc://notificationsoryc://tasks/task123can be opened directly. NotificationNavigationIntentis derived from the messagedatapayload. Supported keys:
| Key | Type | Notes |
|---|---|---|
deepLink | string | Highest priority - opened via Linking.openURL. |
navigationId | enum | Maps to predefined destinations (see table below). |
root | 'Main' | 'Auth' | 'Onboarding' | Optional override for the root navigator. Defaults to Main. |
tab | 'HomeStack' | 'Appointments' | 'Documents' | 'Tasks' | Explicit tab target. |
screen | string | Final stack screen (e.g. Notifications, TaskView). |
params | stringified JSON | Passed through to the target screen. |
category, priority, icon, avatarUrl, relatedId, relatedType | Drive in-app list rendering and navigation context. |
Navigation helpers currently recognise these IDs:
navigationId | Navigates to |
|---|---|
notifications | Home tab → Notifications screen |
tasks | Tasks tab → Tasks main dashboard |
task_detail | Tasks tab → TaskView |
appointments | Appointments tab → MyAppointments |
documents | Documents tab → DocumentsMain |
home | Home tab → Home screen |
1.4 Android specifics
- Manifest includes notification permissions (
POST_NOTIFICATIONS,WAKE_LOCK,RECEIVE) and deep-link intent filter onMainActivity. - Default channel + icon metadata configured inside
<application>. firebase.jsonin repo root points RNFirebase to the custom channel and iOS foreground presentation options.- Gradle includes the Firebase BOM and Messaging dependency.
- Ensure
google-services.jsonsits atapps/mobileAppYC/android/app/google-services.json.
1.5 iOS specifics
- Capabilities enabled:
- Push Notifications
- Background Modes →
Remote notifications&Background fetch
- Entitlements already include
aps-environment=development; update toproductionbefore App Store builds. AppDelegate.swiftnow:- Sets
UNUserNotificationCenter.current().delegate - Sets
Messaging.messaging().delegate - Registers for remote notifications
- Maps APNs (Apple Push Notification service) token → FCM token
- Presents foreground notifications (banner + list + sound + badge)
- Sets
- APNS Key onboarding (if not yet done):
- Apple Developer Console → Keys → create key with APNs enabled.
- Download the
.p8, note Key ID and Team ID. - Firebase Console → Project settings → Cloud Messaging → iOS app → upload APNs key.
ycURL scheme added toInfo.plist.- Run
pod installafter pulling dependencies.
2. Frontend Testing & Verification
2.1 On-device smoke checklist
-
Launch the app → system permission dialog should appear (iOS) or silent grant (Android < 13).
-
Inspect logs to confirm
FCM token updated ...message. -
Trigger a local reminder from any dev console (e.g. Reactotron) or a temporary button:
import { scheduleLocalReminder } from '@/shared/services/firebaseNotifications'; await scheduleLocalReminder('Test Reminder', 'This fired from Notifee after 1 minute', 1, { navigationId: 'notifications', }); -
Verify tapping the notification navigates to the expected screen and the in-app notification list updates instantly.
2.2 Firebase Console
- Project → Cloud Messaging → Send your first message.
- Add Title/Body; under Additional options → Custom data add e.g.
navigationId:notificationscategory:messagespriority:high
- Target your test device (using registration token).
- Observe that the system tray entry appears even when the app is backgrounded or quit, and tapping opens the Notifications screen.
2.3 HTTP v1 / curl sample
curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://fcm.googleapis.com/v1/projects/<your-project-id>/messages:send \
-d '{
"message": {
"token": "<device-token>",
"notification": {
"title": "Yosemite update",
"body": "Tap to review the latest appointment."
},
"data": {
"navigationId": "appointments",
"relatedType": "appointment",
"relatedId": "apt_123",
"category": "appointments",
"priority": "high",
"params": "{\"appointmentId\":\"apt_123\"}"
},
"android": {
"priority": "HIGH"
},
"apns": {
"headers": {
"apns-push-type": "alert",
"apns-priority": "10"
},
"payload": {
"aps": {
"content-available": 1,
"mutable-content": 1
}
}
}
}
}'
2.4 Debugging tips
- Android:
adb logcat ReactNativeJS:I ReactNative:I NOTIFEE:D *:S - iOS: Use Xcode → Devices & Simulators → view device logs filtered by
RNFBorNOTIFE. notifee.getTriggerNotificationIds()andnotifee.getDisplayedNotifications()are useful in debug builds to inspect active entries.
3. Backend Developer Checklist
3.1 Token lifecycle
initializeNotificationscallsonTokenUpdatefor:- First-time registration.
- Token refresh events.
- Action items:
- Provide an authenticated API endpoint:
POST /v1/me/push-tokens{ token, platform }. - Store multiple tokens per user/device (array semantics).
- On logout, call
messaging().deleteToken()from the mobile app and invokeDELETE /v1/me/push-tokens/{token}.
- Provide an authenticated API endpoint:
3.2 Message payload structure
- All
datavalues must be strings (FCM requirement). - Recommended minimum fields:
| Field | Description |
|---|---|
navigationId | Use table above to target in-app location. |
params | JSON string for screen params (e.g. {"taskId":"task_42"}). |
category, priority | Drive feed categorisation & styling. |
companionId | Required to associate with the active pet profile (defaults to 'default-companion' if omitted). |
relatedType, relatedId | Helps analytics + navigation guardrails. |
deepLink (optional) | Overrides navigation with a direct URI. |
3.3 Example Node.js sender
import admin, { messaging } from 'firebase-admin';
admin.initializeApp({
credential: admin.credential.cert(require('./serviceAccount.json')),
});
type YosemiteNotification = {
tokens: string[];
title: string;
body: string;
navigationId: string;
params?: Record<string, unknown>;
companionId: string;
};
export async function sendYosemiteNotification(input: YosemiteNotification) {
const response = await messaging().sendEachForMulticast({
tokens: input.tokens,
notification: {
title: input.title,
body: input.body,
},
data: {
navigationId: input.navigationId,
params: JSON.stringify(input.params ?? {}),
companionId: input.companionId,
category: 'messages',
priority: 'high',
},
android: {
priority: 'high',
},
apns: {
payload: {
aps: {
'content-available': 1,
},
},
headers: {
'apns-push-type': 'alert',
'apns-priority': '10',
},
},
});
console.log('Multicast send summary', response);
}
3.4 Topic & segment messaging
- Devices can subscribe to topics via
messaging().subscribeToTopic(topic)on the app side. - Keep topics generic (no PII, i.e. personally identifiable information). Example:
all-pet-parents,premium-users,city-san-francisco. - Backend APIs:
await messaging().subscribeToTopic(tokens, 'all-pet-parents');
await messaging().send({
topic: 'all-pet-parents',
notification: { title: 'Weekly tips', body: 'Your pet care digest is ready.' },
data: { navigationId: 'home' },
});
4. Future Enhancements & Options
- iOS notification service extension: Needed to display rich media (images) inside alerts. Follow the RNFirebase guide to create a new target and call
Messaging.serviceExtension().populateNotificationContent(...). - Action buttons: Notifee allows adding
pressActionitems (e.g.Mark as read). Extend the background event handler to call the relevant Redux action/API. - In-app preference screen:
messaging().setAutoInitEnabled(false)combined with the Notifee "Open Settings" handler allows fine-grained control for users who disable push permissions. - Analytics: Add
analytics().logEvent('notification_open', { ... })insidenavigateFromNotificationIntentonce the analytics module is enabled.
5. Support Matrix & Known Caveats
- iOS simulator: Push notifications require Apple Silicon + iOS 16+ simulator; otherwise test on a physical device.
- Android 13+: Users can still deny POST_NOTIFICATIONS. Check
await notifee.getNotificationSettings()to guide users if necessary. - Background refresh (iOS): If the user disables Background App Refresh or Low Power Mode is active, background data-only messages may be throttled.
- Notification delegation (Android Q+): React Native Firebase disables the Play Services delegation automatically. If re-enabled in
firebase.json, JS listeners will not fire for delegated messages.
6. Change Log (This Iteration)
- Added Firebase Messaging + Notifee dependencies and bootstrap harness.
- Implemented unified notification service with foreground, background & deep-link handling.
- Registered background handlers in
index.js. - Added Android permissions, default channel metadata, and deep-link intent filter.
- Updated iOS AppDelegate with UNUserNotificationCenter + Messaging delegates and URL scheme.
- Created
firebase.jsonfor messaging defaults. - Introduced helper APIs:
scheduleLocalReminder,clearAllSystemNotifications,getCurrentFcmToken. - Documented backend payload contract and testing workflows (this file).
Happy notifying 🚀