| name | notification-system |
| description | Push notification system using Expo Notifications for train arrival alerts and service disruption notifications. Use when implementing notification features. |
Notification System Guidelines
When to Use
- Setting up push notifications
- Scheduling arrival alerts
- Handling notification permissions
- Managing notification preferences
Setup
Installation
npx expo install expo-notifications expo-device expo-constants
Configuration (app.json)
{
"expo": {
"notification": {
"icon": "./assets/notification-icon.png",
"color": "#0066CC",
"iosDisplayInForeground": true
},
"ios": {
"infoPlist": {
"UIBackgroundModes": ["remote-notification"]
}
},
"android": {
"useNextNotificationsApi": true
}
}
}
Core Patterns
Configure Handler
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
Request Permission
const requestPermission = async (): Promise<boolean> => {
if (!Device.isDevice) return false; // Only physical devices
const { status } = await Notifications.requestPermissionsAsync();
return status === 'granted';
};
Schedule Notification
const scheduleArrivalNotification = async (
stationName: string,
arrivalTime: number
): Promise<string> => {
return await Notifications.scheduleNotificationAsync({
content: {
title: `Train Arriving Soon!`,
body: `Your train to ${stationName} arrives in ${arrivalTime} minutes`,
data: { stationName, arrivalTime },
sound: true,
},
trigger: { seconds: (arrivalTime - 2) * 60 },
});
};
Cancel Notification
await Notifications.cancelScheduledNotificationAsync(notificationId);
await Notifications.cancelAllScheduledNotificationsAsync();
Send Immediate
await Notifications.scheduleNotificationAsync({
content: { title, body, data },
trigger: null, // Send immediately
});
Notification Categories
| Category | Android Importance | Use Case |
|---|---|---|
arrivals |
HIGH | Train arrival alerts |
disruptions |
MAX | Service disruptions |
updates |
DEFAULT | General updates |
User Preferences Pattern
interface NotificationPreferences {
enabled: boolean;
arrivalAlerts: boolean;
serviceDisruptions: boolean;
reminderMinutes: number;
quietHours: {
enabled: boolean;
start: string; // "22:00"
end: string; // "07:00"
};
}
Best Practices
Request Permission at Right Time
// ❌ Bad: On app launch // ✅ Good: When user enables alerts const handleEnableAlerts = async () => { const hasPermission = await requestPermission(); if (hasPermission) { /* enable */ } };Respect User Preferences
- Check
enabledandquietHoursbefore sending - Allow granular control (arrivals vs disruptions)
- Check
Clean Up Old Notifications
- Cancel outdated scheduled notifications
- Clear badge when app opens
Handle Notification Taps
- Parse
datafrom notification - Navigate to relevant screen
- Parse
Error Handling
const handleNotificationError = (error: unknown): string => {
if (error instanceof Error) {
if (error.message.includes('device')) {
return 'Notifications only work on physical devices';
}
if (error.message.includes('permission')) {
return 'Notification permission is required';
}
}
return 'Notification error occurred';
};
Important Notes
- Notifications only work on physical devices (not simulators)
- Always clean up listeners in useEffect return
- Use appropriate channels on Android
- Test notification tap handling thoroughly
- Monitor delivery rates in production
Reference Documentation
For complete implementations, see references/notification-examples.md:
- useNotifications hook
- Android notification channels
- User preferences management
- ArrivalNotificationManager class
- Badge management
- Testing examples