390 lines
11 KiB
Markdown
390 lines
11 KiB
Markdown
# Push Notifications - Mobile App Quick Start
|
|
|
|
This guide helps you integrate push notifications into the ShieldAI React Native mobile app.
|
|
|
|
## Prerequisites
|
|
|
|
Before starting, ensure:
|
|
- ✅ Backend push notification infrastructure is deployed
|
|
- ✅ Firebase project is set up (for Android)
|
|
- ✅ Apple Developer account is active (for iOS)
|
|
- ✅ Environment variables are configured on the backend
|
|
|
|
## Step 1: Install Dependencies
|
|
|
|
```bash
|
|
npm install @react-native-firebase/app @react-native-firebase/messaging
|
|
```
|
|
|
|
## Step 2: Android Setup
|
|
|
|
### 2.1 Add Firebase to Android Project
|
|
|
|
1. Go to [Firebase Console](https://console.firebase.google.com)
|
|
2. Select your ShieldAI project
|
|
3. Add Android app with package name: `com.shieldai.mobile`
|
|
4. Download `google-services.json`
|
|
5. Place it in `android/app/google-services.json`
|
|
|
|
### 2.2 Update Build Configuration
|
|
|
|
In `android/build.gradle`:
|
|
```gradle
|
|
buildscript {
|
|
dependencies {
|
|
classpath 'com.google.gms:google-services:4.4.0'
|
|
}
|
|
}
|
|
```
|
|
|
|
In `android/app/build.gradle`:
|
|
```gradle
|
|
apply plugin: 'com.google.gms.google-services'
|
|
```
|
|
|
|
### 2.3 Request Permissions
|
|
|
|
In `AndroidManifest.xml`:
|
|
```xml
|
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
|
<uses-permission android:name="android.permission.VIBRATE" />
|
|
```
|
|
|
|
## Step 3: iOS Setup
|
|
|
|
### 3.1 Enable Push Notifications
|
|
|
|
1. Open Xcode project
|
|
2. Select your app target
|
|
3. Go to "Signing & Capabilities"
|
|
4. Click "+ Capability"
|
|
5. Add "Push Notifications"
|
|
6. Add "Background Modes" and check "Remote notifications"
|
|
|
|
### 3.2 Configure Firebase
|
|
|
|
1. Download `GoogleService-Info.plist` from Firebase Console
|
|
2. Add it to your Xcode project (drag to project folder)
|
|
3. Ensure "Copy items if needed" is checked
|
|
|
|
### 3.3 Add Import in AppDelegate.swift
|
|
|
|
```swift
|
|
import FirebaseMessaging
|
|
import UserNotifications
|
|
|
|
@main
|
|
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
|
|
|
|
func application(_ application: UIApplication,
|
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
FirebaseApp.configure()
|
|
Messaging.messaging().delegate = self
|
|
return true
|
|
}
|
|
|
|
// Request permission
|
|
func requestAuthorization() {
|
|
UNUserNotificationCenter.current().delegate = self
|
|
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
|
|
UNUserNotificationCenter.current().requestAuthorization(
|
|
options: authOptions,
|
|
completionHandler: { granted, error in
|
|
if granted {
|
|
DispatchQueue.main.async {
|
|
UIApplication.shared.registerForRemoteNotifications()
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
// Handle token refresh
|
|
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
|
|
print("FCM token refreshed: \(fcmToken)")
|
|
// Send new token to backend
|
|
registerDeviceToken(fcmToken)
|
|
}
|
|
|
|
// Handle foreground notifications
|
|
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
willPresent notification: UNNotification,
|
|
withCompletionHandler completionHandler:
|
|
@escaping (UNNotificationPresentationOptions) -> Void) {
|
|
let userInfo = notification.request.content.userInfo
|
|
print("Foreground notification: \(userInfo)")
|
|
completionHandler([.banner, .sound, .badge])
|
|
}
|
|
|
|
// Handle notification tap
|
|
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
didReceive response: UNNotificationResponse,
|
|
withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
let userInfo = response.notification.request.content.userInfo
|
|
print("Notification tapped: \(userInfo)")
|
|
completionHandler()
|
|
}
|
|
}
|
|
|
|
extension AppDelegate: MessagingDelegate {
|
|
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
|
|
print("FCM token received: \(fcmToken ?? "none")")
|
|
if let token = fcmToken {
|
|
registerDeviceToken(token)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function to register with backend
|
|
func registerDeviceToken(_ token: String) {
|
|
// Call your API to register the device
|
|
// POST /api/v1/devices/register
|
|
// Body: { userId, platform: "ios", token, deviceType: "mobile" }
|
|
}
|
|
```
|
|
|
|
## Step 4: React Native Integration
|
|
|
|
### 4.1 Create Notification Service
|
|
|
|
Create `src/services/NotificationService.ts`:
|
|
|
|
```typescript
|
|
import messaging from '@react-native-firebase/messaging';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import axios from 'axios';
|
|
|
|
const API_BASE_URL = 'https://api.shieldai.com/api/v1';
|
|
|
|
export class NotificationService {
|
|
private static instance: NotificationService;
|
|
private userId: string | null = null;
|
|
|
|
private constructor() {}
|
|
|
|
static getInstance(): NotificationService {
|
|
if (!NotificationService.instance) {
|
|
NotificationService.instance = new NotificationService();
|
|
}
|
|
return NotificationService.instance;
|
|
}
|
|
|
|
setUserId(userId: string) {
|
|
this.userId = userId;
|
|
}
|
|
|
|
async requestPermission(): Promise<boolean> {
|
|
try {
|
|
const authStatus = await messaging().requestPermission();
|
|
const enabled =
|
|
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
|
|
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
|
|
|
|
if (enabled) {
|
|
console.log('Push notification permission granted');
|
|
await this.registerDevice();
|
|
return true;
|
|
}
|
|
return false;
|
|
} catch (error) {
|
|
console.error('Failed to request permission:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async registerDevice(): Promise<void> {
|
|
if (!this.userId) {
|
|
console.warn('User ID not set, cannot register device');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const token = await messaging().getToken();
|
|
const platform = Platform.OS === 'ios' ? 'ios' : 'android';
|
|
|
|
const response = await axios.post(`${API_BASE_URL}/devices/register`, {
|
|
userId: this.userId,
|
|
platform,
|
|
token,
|
|
deviceType: 'mobile',
|
|
appName: 'ShieldAI Mobile',
|
|
appVersion: '1.0.0',
|
|
});
|
|
|
|
console.log('Device registered:', response.data);
|
|
} catch (error) {
|
|
console.error('Failed to register device:', error);
|
|
}
|
|
}
|
|
|
|
setupListeners() {
|
|
// Foreground messages
|
|
messaging().onMessage(async (remoteMessage) => {
|
|
console.log('Foreground message received:', remoteMessage);
|
|
// Show local notification
|
|
this.showLocalNotification(remoteMessage);
|
|
});
|
|
|
|
// Background messages
|
|
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
|
|
console.log('Background message received:', remoteMessage);
|
|
});
|
|
|
|
// Notification opened
|
|
messaging().onNotificationOpenedApp((remoteMessage) => {
|
|
console.log('Notification opened app:', remoteMessage);
|
|
// Navigate to relevant screen
|
|
this.handleNotificationTap(remoteMessage);
|
|
});
|
|
|
|
// Check if app was opened from notification
|
|
messaging()
|
|
.getInitialNotification()
|
|
.then((remoteMessage) => {
|
|
if (remoteMessage) {
|
|
console.log('App opened from notification:', remoteMessage);
|
|
this.handleNotificationTap(remoteMessage);
|
|
}
|
|
});
|
|
}
|
|
|
|
private showLocalNotification(message: any) {
|
|
// Use react-native-push-notification or similar
|
|
console.log('Show notification:', message.notification?.title);
|
|
}
|
|
|
|
private handleNotificationTap(message: any) {
|
|
// Navigate to relevant screen based on notification data
|
|
const { alertId, type } = message.data || {};
|
|
if (alertId) {
|
|
// Navigate to alert detail
|
|
console.log('Navigate to alert:', alertId);
|
|
}
|
|
}
|
|
|
|
async deregisterDevice(): Promise<void> {
|
|
if (!this.userId) return;
|
|
|
|
try {
|
|
const token = await messaging().getToken();
|
|
await axios.post(`${API_BASE_URL}/devices/deregister`, {
|
|
token,
|
|
userId: this.userId,
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to deregister device:', error);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4.2 Initialize in App Component
|
|
|
|
```typescript
|
|
// App.tsx
|
|
import React, { useEffect } from 'react';
|
|
import { NotificationService } from './src/services/NotificationService';
|
|
|
|
const notificationService = NotificationService.getInstance();
|
|
|
|
function App() {
|
|
useEffect(() => {
|
|
// Initialize push notifications
|
|
notificationService.setupListeners();
|
|
}, []);
|
|
|
|
// When user logs in
|
|
const handleLogin = async (userId: string) => {
|
|
notificationService.setUserId(userId);
|
|
await notificationService.requestPermission();
|
|
};
|
|
|
|
// When user logs out
|
|
const handleLogout = async () => {
|
|
await notificationService.deregisterDevice();
|
|
notificationService.setUserId('');
|
|
};
|
|
|
|
return (
|
|
// Your app components
|
|
);
|
|
}
|
|
```
|
|
|
|
## Step 5: Testing
|
|
|
|
### Android Emulator
|
|
|
|
1. Start Android emulator with Google Play Services
|
|
2. Install and run app
|
|
3. Trigger a test notification from backend
|
|
4. Check notification appears
|
|
|
|
### iOS Device (Required)
|
|
|
|
iOS Simulator does not support push notifications. Use a real device.
|
|
|
|
### Backend Test
|
|
|
|
```bash
|
|
# Test notification API
|
|
curl -X POST https://api.shieldai.com/api/v1/notifications/send \
|
|
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"userId": "user-uuid",
|
|
"channel": "push",
|
|
"subject": "Test Alert",
|
|
"body": "This is a test notification from ShieldAI",
|
|
"priority": "high",
|
|
"metadata": {
|
|
"alertId": "alert-uuid",
|
|
"type": "darkweb_exposure"
|
|
}
|
|
}'
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Android Issues
|
|
|
|
**Problem**: Token not received
|
|
- Check `google-services.json` is in correct location
|
|
- Verify Firebase project ID matches
|
|
- Check internet connection on emulator
|
|
|
|
**Problem**: Notifications not showing
|
|
- Ensure notification permissions granted
|
|
- Check battery optimization settings
|
|
- Verify notification channel is created
|
|
|
|
### iOS Issues
|
|
|
|
**Problem**: Token not received
|
|
- Verify Push Notifications capability is enabled
|
|
- Check APNs key configuration in backend
|
|
- Ensure device is not in development mode if using production certs
|
|
|
|
**Problem**: Notifications not showing
|
|
- Check notification permissions granted
|
|
- Verify APNs configuration on backend
|
|
- Ensure proper certificate/key is used
|
|
|
|
## Next Steps
|
|
|
|
1. ✅ Set up Firebase project
|
|
2. ✅ Configure APNs on Apple Developer Portal
|
|
3. ✅ Implement notification handling in app
|
|
4. ✅ Test on Android device/emulator
|
|
5. ✅ Test on iOS device
|
|
6. ✅ Integrate with DarkWatch and SpamShield alerts
|
|
7. ✅ Add notification preferences UI
|
|
8. ✅ Implement deep linking from notifications
|
|
|
|
## Resources
|
|
|
|
- [Firebase Cloud Messaging Docs](https://firebase.google.com/docs/cloud-messaging)
|
|
- [React Native Firebase Messaging](https://rnfirebase.io/messaging/usage)
|
|
- [Apple Push Notification Service](https://developer.apple.com/documentation/usernotifications)
|