Firebase Cloud Messaging (FCM) Push Notifications
Overview
Tatu uses Firebase Cloud Messaging (FCM) to send native push notifications to mobile devices (iOS and Android). This system works alongside our existing in-app notification system to provide a complete notification experience.Architecture
Two-Layer System
-
In-App Notifications (Firestore-based)
- Stores notification data and history
- Powers the in-app notification center UI
- Polled every 5 minutes when app is open
- Source of truth for notification state
-
Push Notifications (FCM-based)
- Wakes up devices when app is closed/backgrounded
- Shows native OS notifications
- Triggers app to open and fetch full notification data
- Delivery mechanism only (not source of truth)
How It Works
Implementation
1. User Model
FCM tokens are stored in the user document:- Users can have multiple devices (iPhone, iPad, Android tablet)
- Easy to update individual device tokens
- Simple to remove tokens for specific devices
- Allows per-device tracking and cleanup
2. Backend Implementation
Creating Notifications
ThecreateNotification function automatically sends FCM push notifications:
- FCM sending is non-blocking and won’t fail notification creation
- Firestore write happens first (source of truth)
- FCM failures are logged but don’t throw errors
FCM Utilities
sendPushNotification() - Send to one user
sendPushNotificationToMultipleUsers() - Bulk send
buildNotificationPayload() - Convert notification to FCM format
3. API Endpoints
Register FCM Token
Remove FCM Token
Update Token Usage
4. Frontend/Mobile Implementation
React Hooks
Auto-Registration Hook
Mobile App Integration
React Native (Expo)
iOS Configuration
-
Enable Push Notifications Capability
- In Xcode, select your target
- Go to “Signing & Capabilities”
- Add “Push Notifications” capability
-
Configure APNs
- Create APNs key in Apple Developer Console
- Upload to Firebase Console (Project Settings → Cloud Messaging → iOS)
Android Configuration
-
google-services.json
- Download from Firebase Console
- Place in
android/app/directory
-
Notification Channels (Android 8.0+)
- Already configured in the code above
- Can customize per notification type if needed
Notification Payload Structure
What’s Sent via FCM
Notification Types
All notification types fromNotificationTypeEnum are supported:
- Booking:
NEW_BOOKING_REQUEST,BOOKING_CONFIRMED,BOOKING_CANCELLED, etc. - Session:
SESSION_CONFIRMED,SESSION_COMPLETED, etc. - System:
NEW_FEATURE, etc.
buildNotificationPayload.ts.
Token Management
Token Lifecycle
- Registration - When user logs in or app starts
- Updates - When token changes (rare but possible)
- Removal - When user logs out or revokes permissions
- Cleanup - Invalid tokens automatically removed on send failure
Invalid Token Cleanup
When FCM returns errors (expired/invalid tokens), they’re automatically removed:Manual Cleanup
You can also manually remove tokens:Testing
Local Testing (Development)
-
Use Firebase Console
- Go to Cloud Messaging
- Use “Send test message”
- Enter your device token
- Test different notification types
-
Use Postman/cURL
-
Trigger Real Notifications
- Create a test booking
- Notification will automatically trigger
- Check device for push notification
Production Testing
- Test on physical devices (simulators have limitations)
- Test with app in foreground, background, and closed
- Test notification tapping and deep linking
- Test with multiple devices per user
- Test token cleanup on logout
Troubleshooting
Notifications Not Received
-
Check token registration
-
Verify token in Firestore
- Check user document has
fcmTokensfield - Verify token value matches device
- Check user document has
-
Check Firebase Console logs
- Go to Cloud Functions logs
- Look for “FCM notification sent” messages
- Check for errors
-
Platform-specific issues
- iOS: Verify APNs certificate is uploaded
- Android: Check google-services.json is correct
- Both: Ensure permissions are granted
Tokens Keep Getting Removed
- Check if tokens are valid (not expired)
- Verify device has stable internet connection
- Check Firebase project configuration
Deep Linking Not Working
- Verify notification data includes correct fields
- Check navigation logic in notification handler
- Ensure screens exist in navigation stack
Best Practices
Do’s ✅
- Always register tokens on app start
- Remove tokens on logout
- Handle notification taps for deep linking
- Test on physical devices
- Log FCM operations for debugging
- Use non-blocking FCM sends (current implementation)
Don’ts ❌
- Don’t block notification creation on FCM failures
- Don’t store tokens in frontend state only
- Don’t forget to clean up invalid tokens
- Don’t send sensitive data in FCM payload (use notification ID instead)
- Don’t rely on FCM for notification history (use Firestore)
Future Enhancements
-
Badge Count Management
- Track unread notification count
- Update badge number dynamically
- Sync across devices
-
Rich Notifications
- Include images (tattoo photos)
- Action buttons (Accept/Decline)
- Custom notification sounds
-
Notification Preferences
- Per-notification-type toggles
- Quiet hours settings
- Platform-specific preferences
-
Analytics
- Track notification delivery rates
- Measure tap-through rates
- Monitor token lifecycle
Security Considerations
- FCM tokens are stored in user documents (backend only)
- Tokens are NOT exposed in public user profiles
- Invalid tokens are automatically cleaned up
- Each device has a unique token
- Tokens can be revoked by removing from user document