Lesson 10-React Native Push Notifications

Local Notification Implementation

Basic Local Notifications

import React, { useState } from 'react';
import { View, Button, Platform } from 'react-native';
import PushNotification from 'react-native-push-notification';

// Initialize push notification configuration
PushNotification.configure({
  // (Optional) Called when a notification is clicked
  onNotification: function (notification) {
    console.log('LOCAL NOTIFICATION:', notification);
  },

  // Should the initial notification be popped automatically
  popInitialNotification: true,

  // Should notifications be cleared after being clicked
  requestPermissions: Platform.OS === 'ios', // Request permissions only on iOS
});

const LocalNotificationExample = () => {
  const [notificationCount, setNotificationCount] = useState(0);

  // Trigger a local notification
  const triggerLocalNotification = () => {
    setNotificationCount(notificationCount + 1);

    PushNotification.localNotification({
      title: 'Local Notification', // Notification title
      message: `This is the ${notificationCount + 1}th local notification`, // Notification content
      playSound: true, // Play sound
      soundName: 'default', // Use default sound
      vibrate: true, // Enable vibration
      vibration: 300, // Vibration duration (milliseconds)
      // iOS-specific configuration
      ios: {
        alertAction: 'view', // Alert action button text
        userInfo: { // Custom data
          id: 'local-notification',
        },
      },
      // Android-specific configuration
      android: {
        channelId: 'default-channel-id', // Notification channel ID (required for Android 8.0+)
        smallIcon: 'ic_notification', // Notification small icon (must be defined in Android resources)
        largeIcon: 'ic_launcher', // Notification large icon
        color: '#FF0000', // Notification color
        actions: ['Yes', 'No'], // Notification action buttons
      },
    });
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title={`Trigger Local Notification (Triggered ${notificationCount} times)`}
        onPress={triggerLocalNotification}
      />
    </View>
  );
};

export default LocalNotificationExample;

Scheduled Local Notifications

import React from 'react';
import { View, Button } from 'react-native';
import PushNotification from 'react-native-push-notification';

// Initialize configuration (same as above)

const ScheduledNotificationExample = () => {
  // Trigger immediate notification
  const triggerImmediateNotification = () => {
    PushNotification.localNotification({
      title: 'Immediate Notification',
      message: 'This notification will display immediately',
    });
  };

  // Schedule notification after 10 seconds
  const scheduleDelayedNotification = () => {
    PushNotification.localNotificationSchedule({
      message: 'This notification will display in 10 seconds',
      date: new Date(Date.now() + 10 * 1000), // 10 seconds from now
    });
  };

  // Schedule daily notification at 9 AM
  const scheduleDailyNotification = () => {
    PushNotification.localNotificationSchedule({
      message: 'Daily reminder at 9 AM',
      date: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours from now (example only)
      repeatType: 'day', // Repeat type: 'day', 'hour', 'minute', 'week', 'month', 'year'
      repeatTime: 24 * 60 * 60 * 1000, // Repeat interval (milliseconds)
    });
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button title="Immediate Notification" onPress={triggerImmediateNotification} />
      <Button title="10 Seconds Later Notification" onPress={scheduleDelayedNotification} />
      <Button title="Daily 9 AM Notification" onPress={scheduleDailyNotification} />
    </View>
  );
};

export default ScheduledNotificationExample;

Remote Push Notifications

Firebase Cloud Messaging (FCM) Integration

// 1. Install dependencies
// npm install @react-native-firebase/app @react-native-firebase/messaging

// 2. Initialize Firebase and messaging service
import React, { useEffect } from 'react';
import { View, Button, Platform, Alert } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import notifee, { AndroidImportance } from '@notifee/react-native'; // For advanced notification control

const RemoteNotificationExample = () => {
  useEffect(() => {
    // Request notification permission
    requestUserPermission();

    // Listen for messages received in the foreground
    messaging().onMessage(async (remoteMessage) => {
      console.log('Foreground message received:', remoteMessage);
      // Display custom notification in foreground
      displayNotification(remoteMessage);
    });

    // Listen for notifications opened from the background
    messaging().onNotificationOpenedApp(remoteMessage => {
      console.log('App opened from background:', remoteMessage);
      // Handle notification click when app is opened from background
    });

    // Check if app was opened by a notification
    messaging()
      .getInitialNotification()
      .then(remoteMessage => {
        if (remoteMessage) {
          console.log('App opened by notification:', remoteMessage);
          // Handle notification when app is opened
        }
      });

    // Listen for token refresh
    messaging().onTokenRefresh(token => {
      console.log('FCM Token:', token);
      // Send token to your server
    });

    return () => {
      // Clean up listeners
    };
  }, []);

  // Request user permission
  const requestUserPermission = async () => {
    const authStatus = await messaging().requestPermission();
    const enabled =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL;

    if (enabled) {
      console.log('Authorization successful');
      // Get FCM token
      getFCMToken();
    } else {
      console.log('Authorization denied');
    }
  };

  // Get FCM token
  const getFCMToken = async () => {
    try {
      const token = await messaging().getToken();
      console.log('FCM Token:', token);
      // Send token to your server
    } catch (error) {
      console.error('Failed to get FCM token:', error);
    }
  };

  // Display custom notification (using Notifee library)
  const displayNotification = (remoteMessage) => {
    // Android-specific configuration
    if (Platform.OS === 'android') {
      notifee.createChannel({
        id: 'default',
        name: 'Default Notifications',
        importance: AndroidImportance.HIGH,
      });
    }

    // Display notification
    notifee.displayNotification({
      title: remoteMessage.notification.title,
      body: remoteMessage.notification.body,
      android: {
        channelId: 'default',
        smallIcon: 'ic_notification', // Android notification icon
      },
      ios: {
        sound: 'default', // iOS notification sound
      },
    });
  };

  // Simulate remote notification test (normally sent by server)
  const simulateRemoteNotification = () => {
    // This is a simulation; actual notifications are sent by FCM server
    const mockMessage = {
      notification: {
        title: 'Simulated Remote Notification',
        body: 'This is a simulated remote notification from the app',
      },
      data: {
        customKey: 'customValue',
      },
    };

    // Display notification in foreground
    displayNotification(mockMessage);
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Simulate Remote Notification"
        onPress={simulateRemoteNotification}
      />
    </View>
  );
};

export default RemoteNotificationExample;

Handling Background Notifications

import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import messaging from '@react-native-firebase/messaging';

const BackgroundNotificationHandler = () => {
  useEffect(() => {
    // Listen for messages received in the background
    messaging().setBackgroundMessageHandler(async (remoteMessage) => {
      console.log('Background message received:', remoteMessage);
      // Note: Cannot directly display UI in background; perform background tasks
      // such as storing data or updating app state
    });

    return () => {
      // Clean up listeners
    };
  }, []);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Background Notification Handler Example</Text>
      <Text>Note: Background notification handling occurs in native code layer</Text>
    </View>
  );
};

export default BackgroundNotificationHandler;

Notification Channels and Categories (Android)

Creating Notification Channels

import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import notifee, { AndroidImportance } from '@notifee/react-native';

const NotificationChannelExample = () => {
  // Create notification channels
  const createNotificationChannel = async () => {
    await notifee.createChannel({
      id: 'important-notifications',
      name: 'Important Notifications',
      importance: AndroidImportance.HIGH,
      vibrationPattern: [0, 500, 250, 500], // Custom vibration pattern
      lightColor: '#FF00FF', // LED light color
      sound: 'default', // Default sound
    });

    await notifee.createChannel({
      id: 'low-priority-notifications',
      name: 'Low Priority Notifications',
      importance: AndroidImportance.LOW,
    });

    console.log('Notification channels created successfully');
  };

  // Send notification to specific channel
  const sendToSpecificChannel = async () => {
    await notifee.displayNotification({
      title: 'Important Notification',
      body: 'This notification is sent through the important notifications channel',
      android: {
        channelId: 'important-notifications',
        pressAction: {
          id: 'default',
          launchActivity: 'com.example.MainActivity', // Activity to open on notification click
        },
      },
    });
  };

  useEffect(() => {
    createNotificationChannel();
  }, []);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Send to Important Notifications Channel"
        onPress={sendToSpecificChannel}
      />
    </View>
  );
};

export default NotificationChannelExample;

iOS-Specific Configuration

iOS Notification Permissions and Configuration

import React, { useEffect } from 'react';
import { View, Button, Platform } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import notifee, { IOSImportance } from '@notifee/react-native';

const IOSNotificationExample = () => {
  useEffect(() => {
    // iOS-specific permission request
    if (Platform.OS === 'ios') {
      requestIOSPermissions();
    }

    // Listen for notifications (same as Android)
    messaging().onMessage(async (remoteMessage) => {
      console.log('iOS foreground notification:', remoteMessage);
      displayIOSNotification(remoteMessage);
    });

    return () => {};
  }, []);

  // Request iOS permissions
  const requestIOSPermissions = async () => {
    const authStatus = await messaging().requestPermission({
      alert: true,
      badge: true,
      sound: true,
      provisional: false, // Whether to request provisional permissions (no alert prompt)
    });

    if (authStatus === messaging.AuthorizationStatus.AUTHORIZED) {
      console.log('iOS notification permission granted');
      getFCMToken();
    } else if (authStatus === messaging.AuthorizationStatus.PROVISIONAL) {
      console.log('iOS provisional notification permission granted');
    } else {
      console.log('iOS notification permission denied');
    }
  };

  // Get FCM token (same as Android)
  const getFCMToken = async () => {
    const token = await messaging().getToken();
    console.log('iOS FCM Token:', token);
  };

  // Display iOS notification
  const displayIOSNotification = (remoteMessage) => {
    notifee.displayNotification({
      title: remoteMessage.notification.title,
      body: remoteMessage.notification.body,
      ios: {
        sound: 'default',
        categoryId: 'CATEGORY_ID', // For interactive notifications
      },
    });
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="iOS Notification Example"
        onPress={() => console.log('iOS notification functionality requires real device testing')}
      />
    </View>
  );
};

export default IOSNotificationExample;

Interactive Notifications (iOS)

import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import notifee, { IOSActionType } from '@notifee/react-native';

const InteractiveNotificationExample = () => {
  // Create interactive notification
  const createInteractiveNotification = async () => {
    // First create notification channel (if not already created)
    await notifee.createChannel({
      id: 'interactive-channel',
      name: 'Interactive Notifications',
    });

    // Register notification actions
    await notifee.setNotificationCategories([
      {
        id: 'CATEGORY_ID',
        actions: [
          {
            id: 'like-action',
            title: 'Like',
            type: IOSActionType.BUTTON,
          },
          {
            id: 'comment-action',
            title: 'Comment',
            type: IOSActionType.TEXTINPUT, // Text input action
          },
          {
            id: 'dismiss-action',
            title: 'Dismiss',
            type: IOSActionType.BUTTON,
            requiresAuthentication: true, // Requires device unlock
          },
        ],
      },
    ]);

    // Display interactive notification
    await notifee.displayNotification({
      title: 'Interactive Notification Example',
      body: 'Click buttons or enter a comment',
      android: {
        channelId: 'interactive-channel',
      },
      ios: {
        categoryId: 'CATEGORY_ID',
        sound: 'default',
      },
    });
  };

  useEffect(() => {
    createInteractiveNotification();
  }, []);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Display Interactive Notification"
        onPress={createInteractiveNotification}
      />
    </View>
  );
};

export default InteractiveNotificationExample;

Advanced Notification Features

Custom Notification Layout

import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import notifee, { AndroidImportance } from '@notifee/react-native';

const CustomLayoutNotificationExample = () => {
  // Create custom layout notification (Android)
  const showCustomLayoutNotification = async () => {
    // Create big text style notification
    await notifee.displayNotification({
      title: 'Big Text Notification',
      body: 'This is summary text',
      android: {
        channelId: 'default',
        style: {
          type: notifee.AndroidStyle.BIGTEXT,
          bigText: 'This is big text content, which can display multiple lines of details...\nSecond line\nThird line',
          summaryText: 'Summary text',
        },
      },
    });

    // Create inbox style notification
    await notifee.displayNotification({
      title: 'Inbox Notification',
      body: 'You have new messages',
      android: {
        channelId: 'default',
        style: {
          type: notifee.AndroidStyle.INBOX,
          lines: [
            'Message 1: This is the first message',
            'Message 2: This is the second message',
            'Message 3: This is the third message',
          ],
        },
      },
    });

    // Create image style notification
    await notifee.displayNotification({
      title: 'Image Notification',
      body: 'Notification with an image',
      android: {
        channelId: 'default',
        image: 'file:///sdcard/Download/notification-image.jpg', // Local image path
      },
    });
  };

  useEffect(() => {
    // Ensure channel is created
    notifee.createChannel({
      id: 'default',
      name: 'Default Notifications',
      importance: AndroidImportance.HIGH,
    });
  }, []);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Display Custom Layout Notification"
        onPress={showCustomLayoutNotification}
      />
    </View>
  );
};

export default CustomLayoutNotificationExample;

Notification Actions and User Interaction

import React, { useEffect, useState } from 'react';
import { View, Button, Alert } from 'react-native';
import notifee, { AndroidImportance } from '@notifee/react-native';

const NotificationActionsExample = () => {
  const [notificationResponse, setNotificationResponse] = useState(null);

  // Create notification with action buttons
  const createNotificationWithActions = async () => {
    await notifee.createChannel({
      id: 'actions-channel',
      name: 'Notifications with Actions',
    });

    await notifee.displayNotification({
      title: 'Notification with Actions',
      body: 'Click buttons to perform actions',
      android: {
        channelId: 'actions-channel',
        actions: [
          {
            title: 'Confirm',
            pressAction: {
              id: 'confirm-action',
            },
          },
          {
            title: 'Cancel',
            pressAction: {
              id: 'cancel-action',
            },
          },
        ],
      },
    });
  };

  // Listen for notification actions
  useEffect(() => {
    const unsubscribe = notifee.onForegroundAction(({ type, detail }) => {
      if (type === notifee.ForegroundActionType.PRESS) {
        console.log('Notification clicked:', detail.pressAction?.id);
        setNotificationResponse(detail.pressAction?.id);
        
        // Perform different logic based on action ID
        if (detail.pressAction?.id === 'confirm-action') {
          Alert.alert('Confirm', 'You clicked the Confirm button');
        } else if (detail.pressAction?.id === 'cancel-action') {
          Alert.alert('Cancel', 'You clicked the Cancel button');
        }
      }
    });

    return () => unsubscribe();
  }, []);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Create Notification with Actions"
        onPress={createNotificationWithActions}
      />
      {notificationResponse && (
        <Text>User Action: {notificationResponse}</Text>
      )}
    </View>
  );
};

export default NotificationActionsExample;

Custom Notification Sound and Vibration

import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import notifee, { AndroidImportance } from '@notifee/react-native';

const CustomSoundVibrationExample = () => {
  // Create notification with custom sound
  const createCustomSoundNotification = async () => {
    // Sound file must be placed in Android's res/raw directory or iOS's Bundle
    await notifee.displayNotification({
      title: 'Custom Sound Notification',
      body: 'This notification uses a custom sound',
      android: {
        channelId: 'default',
        sound: 'custom_sound', // Android: res/raw/custom_sound.mp3
      },
      ios: {
        sound: 'custom_sound.aiff', // iOS: Sound file in Bundle
      },
    });
  };

  // Create notification with custom vibration pattern
  const createCustomVibrationNotification = async () => {
    await notifee.displayNotification({
      title: 'Custom Vibration Notification',
      body: 'This notification uses a custom vibration pattern',
      android: {
        channelId: 'default',
        vibrationPattern: [0, 200, 100, 500], // Vibration pattern: [delay, vibrate, delay, vibrate...]
      },
    });
  };

  useEffect(() => {
    // Ensure channel is created
    notifee.createChannel({
      id: 'default',
      name: 'Default Notifications',
      importance: AndroidImportance.HIGH,
    });
  }, []);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Custom Sound Notification"
        onPress={createCustomSoundNotification}
      />
      <Button
        title="Custom Vibration Notification"
        onPress={createCustomVibrationNotification}
      />
    </View>
  );
};

export default CustomSoundVibrationExample;
Share your love