Skip to main content

Push Notifications Guide

Guide to implement push notifications in 54VIE Super App.

Setup

iOS (APNs)

  1. Enable Push Notifications capability in Xcode
  2. Configure APNs key in Apple Developer Portal
  3. Upload key to Firebase Console

Android (FCM)

  1. Add google-services.json to android/app/
  2. 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);