Files
Kordant/docs/MOBILE_PUSH_INTEGRATION.md

11 KiB

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

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
  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:

buildscript {
  dependencies {
    classpath 'com.google.gms:google-services:4.4.0'
  }
}

In android/app/build.gradle:

apply plugin: 'com.google.gms.google-services'

2.3 Request Permissions

In AndroidManifest.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

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:

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

// 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

# 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