Push Notifications Guide
Guide to implement push notifications in 54VIE Super App.
Setup
iOS (APNs)
- Enable Push Notifications capability in Xcode
- Configure APNs key in Apple Developer Portal
- Upload key to Firebase Console
Android (FCM)
- Add
google-services.jsontoandroid/app/ - Configure Firebase project
Host App
import { PushProvider } from '@54vie/push';
function App() {
return (
<PushProvider
onNotificationReceived={(notification) => {
console.log('Received:', notification);
}}
onNotificationOpened={(notification) => {
handleDeepLink(notification.data);
}}
>
<MainApp />
</PushProvider>
);
}
Request Permission
import { usePush } from '@54vie/push';
function NotificationSetup() {
const { isPermissionGranted, requestPermission, token } = usePush();
useEffect(() => {
if (!isPermissionGranted) {
requestPermission();
}
}, []);
useEffect(() => {
if (token) {
// Register token with server
api.post('/devices/register', { token, platform: Platform.OS });
}
}, [token]);
}
Topic Subscription
import { pushService } from '@54vie/push';
// Subscribe to topics
await pushService.subscribeToTopic('promotions');
await pushService.subscribeToTopic(`user_${userId}`);
await pushService.subscribeToTopic('news');
// Unsubscribe
await pushService.unsubscribeFromTopic('promotions');
Local Notifications
Schedule
import { useLocalNotification } from '@54vie/push';
function OrderScreen() {
const { schedule, cancel } = useLocalNotification();
const schedulePickupReminder = async (orderId: string) => {
const notificationId = await schedule({
title: 'Order Ready!',
body: 'Your order is ready for pickup',
data: { orderId, action: 'pickup' },
scheduledAt: new Date(Date.now() + 30 * 60 * 1000), // 30 min
});
// Save ID to cancel later if needed
await storage.set(`reminder_${orderId}`, notificationId);
};
const cancelReminder = async (orderId: string) => {
const notificationId = await storage.get(`reminder_${orderId}`);
if (notificationId) {
cancel(notificationId);
}
};
}
Recurring
// Daily reminder
await schedule({
title: 'Daily Check-in',
body: 'Open the app to earn points!',
repeatType: 'day',
scheduledAt: new Date().setHours(9, 0, 0), // 9 AM daily
});
// Weekly
await schedule({
title: 'Weekly Summary',
body: 'Check your weekly activity',
repeatType: 'week',
scheduledAt: new Date(), // Same time next week
});
Action Buttons
import { localNotificationService } from '@54vie/push';
// Create category with actions
await localNotificationService.createCategory('order', [
{ id: 'accept', title: 'Accept', destructive: false },
{ id: 'reject', title: 'Reject', destructive: true },
]);
// Schedule with category
await schedule({
title: 'New Order',
body: 'Order #123 - $50.00',
category: 'order',
data: { orderId: '123' },
});
// Handle action
PushProvider.onNotificationAction((action, notification) => {
if (action === 'accept') {
acceptOrder(notification.data.orderId);
} else if (action === 'reject') {
rejectOrder(notification.data.orderId);
}
});
Notification Center
import { useNotifications, NotificationList } from '@54vie/push';
function NotificationsScreen() {
const {
notifications,
unreadCount,
markAsRead,
markAllAsRead,
deleteNotification,
} = useNotifications();
return (
<View>
<Header>
<Text>Notifications ({unreadCount} unread)</Text>
<Button onPress={markAllAsRead}>Mark all read</Button>
</Header>
<NotificationList
notifications={notifications}
onPress={(notification) => {
markAsRead(notification.id);
navigateToContent(notification.data);
}}
onDelete={(notification) => {
deleteNotification(notification.id);
}}
/>
</View>
);
}
Deep Linking
import { DeepLinkRouter } from '@54vie/push';
// Register routes
DeepLinkRouter.register('order/:orderId', ({ orderId }) => {
navigation.navigate('OrderDetail', { id: orderId });
});
DeepLinkRouter.register('miniapp/:appId', ({ appId, ...params }) => {
navigation.navigate('MiniProgram', { appId, params });
});
DeepLinkRouter.register('promo/:promoId', ({ promoId }) => {
navigation.navigate('PromoDetail', { id: promoId });
});
// In notification handler
onNotificationOpened((notification) => {
if (notification.data?.deepLink) {
DeepLinkRouter.navigate(notification.data.deepLink);
}
});
Android Channels
import { NotificationChannel } from '@54vie/push';
// Create channels at app start
NotificationChannel.create({
id: 'payments',
name: 'Payment Notifications',
description: 'Notifications about payments and transactions',
importance: 'high',
sound: 'payment_sound',
vibration: true,
});
NotificationChannel.create({
id: 'promotions',
name: 'Promotions',
description: 'Special offers and promotions',
importance: 'default',
});
NotificationChannel.create({
id: 'chat',
name: 'Messages',
description: 'Chat messages',
importance: 'high',
sound: 'message_sound',
});
Mini-App Push
// In mini-app
import { push, useHostPush } from '@54vie/miniapp-sdk';
function MiniAppNotifications() {
const { hasPermission, requestPermission } = useHostPush();
// Subscribe to mini-app specific topic
useEffect(() => {
push.subscribeToTopic(`miniapp_${MINI_APP_ID}_updates`);
}, []);
// Schedule local notification
const scheduleReminder = async () => {
await push.scheduleNotification({
title: 'Reminder from Shopping',
body: 'Items in your cart are selling fast!',
data: { miniAppId: MINI_APP_ID, action: 'view_cart' },
scheduledAt: new Date(Date.now() + 60 * 60 * 1000),
});
};
}
Server-Side Push
Send notification
curl -X POST https://fcm.googleapis.com/fcm/send \
-H "Authorization: key=$SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "$DEVICE_TOKEN",
"notification": {
"title": "New Order",
"body": "You have a new order!"
},
"data": {
"orderId": "123",
"deepLink": "54vie://order/123"
}
}'
Send to topic
curl -X POST https://fcm.googleapis.com/fcm/send \
-H "Authorization: key=$SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "/topics/promotions",
"notification": {
"title": "Flash Sale!",
"body": "50% off everything for 2 hours"
},
"data": {
"deepLink": "54vie://promo/flash-sale"
}
}'
Best Practices
1. Request permission at the right time
// ✅ Good - after user action or onboarding
const handleEnableNotifications = async () => {
const granted = await requestPermission();
if (granted) {
Toast.success('Notifications enabled!');
}
};
// ❌ Bad - immediately on app launch
useEffect(() => {
requestPermission(); // User might decline
}, []);
2. Provide value in notifications
// ✅ Good - actionable and relevant
{
title: 'Your order is out for delivery',
body: 'Estimated arrival: 2:30 PM',
data: { orderId: '123', trackingUrl: '...' }
}
// ❌ Bad - generic
{
title: 'Check out our app!',
body: 'Open now'
}
3. Handle notification state
// ✅ Good - handle all states
PushProvider
.onNotificationReceived(handleForeground)
.onNotificationOpened(handleBackground)
.onInitialNotification(handleColdStart);