A comprehensive guide to implementing push notifications in your mobile app using Firebase Cloud Messaging (FCM)
Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably send notifications at no cost. Using FCM, you can notify a client app that new email or other data is available to sync. You can send notification messages to drive user re-engagement and retention.
Before implementing FCM, ensure you have:
First, you need to add Firebase to your project. The process varies depending on your platform:
google-services.json file and place it in your app's module directorydependencies {
// Add the Firebase SDK for Google Analytics
implementation 'com.google.firebase:firebase-analytics:21.3.0'
// Add the Firebase Cloud Messaging SDK
implementation 'com.google.firebase:firebase-messaging:23.2.0'
}
And at the bottom of the file, add:
apply plugin: 'com.google.gms.google-services'
In your project-level build.gradle file, add:
buildscript {
repositories {
google()
// ...
}
dependencies {
// ...
classpath 'com.google.gms:google-services:4.3.15'
}
}
GoogleService-Info.plist filepod 'Firebase/Analytics'
pod 'Firebase/Messaging'
Then run:
pod install
In your AppDelegate, initialize Firebase:
import Firebase
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
return true
}
}
<!-- Insert these scripts at the bottom of the HTML, but before you use any Firebase services -->
<script src="https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging.js"></script>
<script>
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "your-api-key",
authDomain: "your-auth-domain",
projectId: "your-project-id",
storageBucket: "your-storage-bucket",
messagingSenderId: "your-messaging-sender-id",
appId: "your-app-id"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
</script>
Before you can send notifications to a device, you need to request permission from the user and get an FCM token.
In your application class or main activity:
import com.google.firebase.messaging.FirebaseMessaging;
// Get FCM token
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (!task.isSuccessful()) {
Log.w(TAG, "Fetching FCM registration token failed", task.getException());
return;
}
// Get new FCM registration token
String token = task.getResult();
// Log and toast
Log.d(TAG, "FCM Token: " + token);
// Send token to your server
sendRegistrationToServer(token);
});
In your AppDelegate:
import Firebase
import FirebaseMessaging
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
// Request permission for notifications
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: { _, _ in }
)
application.registerForRemoteNotifications()
// Set messaging delegate
Messaging.messaging().delegate = self
return true
}
// Receive FCM token
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("Firebase registration token: \(String(describing: fcmToken))")
// Send token to your server
if let token = fcmToken {
sendRegistrationToServer(token)
}
}
}
// Request permission
const messaging = firebase.messaging();
messaging.requestPermission()
.then(() => {
console.log('Notification permission granted.');
return messaging.getToken();
})
.then((token) => {
console.log('FCM Token:', token);
// Send token to your server
sendTokenToServer(token);
})
.catch((err) => {
console.log('Unable to get permission to notify.', err);
});
First, add the Firebase Messaging package:
flutter pub add firebase_messaging
Then in your app:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
@override
void initState() {
super.initState();
setupFirebaseMessaging();
}
Future setupFirebaseMessaging() async {
// Request permission
NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
alert: true,
badge: true,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
// Get token
String? token = await FirebaseMessaging.instance.getToken();
print('FCM Token: $token');
// Send token to server
if (token != null) {
sendTokenToServer(token);
}
// Listen for token refresh
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
print('FCM Token refreshed: $newToken');
sendTokenToServer(newToken);
});
}
void sendTokenToServer(String token) {
// Implement your server communication here
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('FCM Example')),
body: Center(child: Text('Firebase Cloud Messaging Example')),
),
);
}
}
First, install the required packages:
npm install @react-native-firebase/app @react-native-firebase/messaging
Then in your app:
import React, { useEffect } from 'react';
import { Alert } from 'react-native';
import messaging from '@react-native-firebase/messaging';
function App() {
useEffect(() => {
// Request permission
const requestUserPermission = async () => {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Authorization status:', authStatus);
getFcmToken();
}
}
// Get FCM token
const getFcmToken = async () => {
const fcmToken = await messaging().getToken();
if (fcmToken) {
console.log('FCM Token:', fcmToken);
// Send token to your server
sendTokenToServer(fcmToken);
}
}
requestUserPermission();
// Listen to token refresh
const unsubscribe = messaging().onTokenRefresh(token => {
console.log('FCM Token refreshed:', token);
sendTokenToServer(token);
});
return unsubscribe;
}, []);
const sendTokenToServer = (token) => {
// Implement your server communication here
};
return (
// Your app component
);
}
export default App;
Now that you have set up FCM and obtained a token, you need to handle incoming messages.
Create a service that extends FirebaseMessagingService:
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Check if message contains a notification payload
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
// Show notification
sendNotification(remoteMessage.getNotification().getTitle(),
remoteMessage.getNotification().getBody());
}
// Check if message contains a data payload
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
// Handle data payload
handleDataMessage(remoteMessage.getData());
}
}
@Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
// Send token to your app server
sendRegistrationToServer(token);
}
private void sendNotification(String title, String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_IMMUTABLE);
String channelId = "fcm_default_channel";
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0, notificationBuilder.build());
}
private void handleDataMessage(Map data) {
// Process data message here
}
private void sendRegistrationToServer(String token) {
// Implement this method to send token to your app server
}
}
Don't forget to register the service in your AndroidManifest.xml:
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
In your AppDelegate, implement the UNUserNotificationCenterDelegate methods:
// Handle notification when app is in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// With swizzling disabled you must let Messaging know about the message
Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID
if let messageID = userInfo["gcm.message_id"] {
print("Message ID: \(messageID)")
}
// Print full message
print(userInfo)
// Change this to your preferred presentation option
completionHandler([[.alert, .sound]])
}
// Handle notification when user taps on it
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID
if let messageID = userInfo["gcm.message_id"] {
print("Message ID: \(messageID)")
}
// Print full message
print(userInfo)
completionHandler()
}
// Handle receiving message
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// With swizzling disabled you must let Messaging know about the message
Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID
if let messageID = userInfo["gcm.message_id"] {
print("Message ID: \(messageID)")
}
// Print full message
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
// Handle foreground messages
messaging.onMessage((payload) => {
console.log('Message received in foreground:', payload);
// Display notification
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
icon: '/firebase-logo.png'
};
if (Notification.permission === 'granted') {
new Notification(notificationTitle, notificationOptions);
}
});
// Handle background messages
// This requires a service worker
// Create a file named firebase-messaging-sw.js in your web root:
/*
// firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging.js');
firebase.initializeApp({
apiKey: "your-api-key",
authDomain: "your-auth-domain",
projectId: "your-project-id",
storageBucket: "your-storage-bucket",
messagingSenderId: "your-messaging-sender-id",
appId: "your-app-id"
});
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
console.log('Background message received:', payload);
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
icon: '/firebase-logo.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
*/
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
// Define a top-level function to handle background messages
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
@override
void initState() {
super.initState();
setupFirebaseMessaging();
setupLocalNotifications();
}
Future setupFirebaseMessaging() async {
// Set the background message handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// Handle foreground messages
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print("Got a message whilst in the foreground!");
print("Message data: ${message.data}");
if (message.notification != null) {
print("Message also contained a notification: ${message.notification}");
showNotification(message);
}
});
// Handle when the app is opened from a notification
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('A new onMessageOpenedApp event was published!');
// Navigate to relevant page based on message data
handleNotificationClick(message.data);
});
// Check if the app was opened from a notification
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
handleNotificationClick(initialMessage.data);
}
}
void setupLocalNotifications() {
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings();
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String? payload) async {
if (payload != null) {
// Handle notification click with payload
Map data = json.decode(payload);
handleNotificationClick(data);
}
});
}
void showNotification(RemoteMessage message) async {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null) {
await flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
'channel_id',
'channel_name',
channelDescription: 'channel_description',
importance: Importance.max,
priority: Priority.high,
showWhen: false,
),
iOS: IOSNotificationDetails(),
),
payload: json.encode(message.data),
);
}
}
void handleNotificationClick(Map data) {
// Navigate to relevant page based on notification data
print('Handling notification click with data: $data');
// Example: Navigator.pushNamed(context, '/details', arguments: data);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('FCM Example')),
body: Center(child: Text('Firebase Cloud Messaging Example')),
),
);
}
}
import React, { useEffect } from 'react';
import { Alert } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import PushNotification from 'react-native-push-notification';
function App() {
useEffect(() => {
// Configure local notifications
PushNotification.configure({
onNotification: function (notification) {
console.log('NOTIFICATION:', notification);
// Process the notification
// Required on iOS only
notification.finish(PushNotificationIOS.FetchResult.NoData);
},
// IOS ONLY
permissions: {
alert: true,
badge: true,
sound: true,
},
popInitialNotification: true,
requestPermissions: true,
});
// Create notification channel for Android
PushNotification.createChannel(
{
channelId: "fcm_default_channel",
channelName: "Default Channel",
channelDescription: "A default channel for notifications",
soundName: "default",
importance: 4,
vibrate: true,
},
(created) => console.log(`Channel created: ${created}`)
);
// Handle foreground messages
const unsubscribe = messaging().onMessage(async remoteMessage => {
console.log('A new FCM message arrived!', remoteMessage);
// Display local notification
PushNotification.localNotification({
channelId: "fcm_default_channel",
title: remoteMessage.notification?.title,
message: remoteMessage.notification?.body || '',
data: remoteMessage.data,
});
});
// Handle background/quit state messages
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
});
// Check if app was opened from a notification
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log(
'Notification caused app to open from quit state:',
remoteMessage,
);
// Navigate based on the notification data
handleNotificationOpen(remoteMessage);
}
});
// Handle notification open when app is in background
messaging().onNotificationOpenedApp(remoteMessage => {
console.log(
'Notification caused app to open from background state:',
remoteMessage,
);
// Navigate based on the notification data
handleNotificationOpen(remoteMessage);
});
return unsubscribe;
}, []);
const handleNotificationOpen = (remoteMessage) => {
// Navigate to relevant screen based on notification data
console.log('Handling notification open with data:', remoteMessage.data);
// Example: navigation.navigate('Details', { id: remoteMessage.data.id });
};
return (
// Your app component
);
}
export default App;
When a client app registers with FCM, it receives a registration token. You should store this token on your server to send messages to specific devices.
// Example server code (Node.js with Express and MongoDB)
const express = require('express');
const mongoose = require('mongoose');
const app = express();
app.use(express.json());
// Define a schema for storing FCM tokens
const tokenSchema = new mongoose.Schema({
userId: { type: String, required: true },
token: { type: String, required: true },
platform: { type: String, enum: ['android', 'ios', 'web'], required: true },
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
const Token = mongoose.model('Token', tokenSchema);
// API endpoint to register/update FCM token
app.post('/api/register-token', async (req, res) => {
try {
const { userId, token, platform } = req.body;
// Validate input
if (!userId || !token || !platform) {
return res.status(400).json({ error: 'Missing required fields' });
}
// Update token if it exists, otherwise create a new one
const result = await Token.findOneAndUpdate(
{ userId, platform },
{ token, updatedAt: Date.now() },
{ upsert: true, new: true }
);
res.status(200).json({ success: true, data: result });
} catch (error) {
console.error('Error registering token:', error);
res.status(500).json({ error: 'Server error' });
}
});
You can send messages to devices using the FCM HTTP v1 API or the Firebase Admin SDK.
Using the Firebase Admin SDK:
const admin = require('firebase-admin');
const serviceAccount = require('./path/to/serviceAccountKey.json');
// Initialize the app with a service account
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
// Send a message to a specific device
async function sendNotificationToDevice(token, title, body, data = {}) {
try {
const message = {
notification: {
title,
body
},
data,
token
};
const response = await admin.messaging().send(message);
console.log('Successfully sent message:', response);
return response;
} catch (error) {
console.error('Error sending message:', error);
throw error;
}
}
// Send a message to multiple devices
async function sendNotificationToDevices(tokens, title, body, data = {}) {
try {
const message = {
notification: {
title,
body
},
data,
tokens // Array of tokens
};
const response = await admin.messaging().sendMulticast(message);
console.log(`${response.successCount} messages were sent successfully`);
return response;
} catch (error) {
console.error('Error sending messages:', error);
throw error;
}
}
// Send a message to a topic
async function sendNotificationToTopic(topic, title, body, data = {}) {
try {
const message = {
notification: {
title,
body
},
data,
topic
};
const response = await admin.messaging().send(message);
console.log('Successfully sent message to topic:', response);
return response;
} catch (error) {
console.error('Error sending message to topic:', error);
throw error;
}
}
// Example API endpoint to send notification
app.post('/api/send-notification', async (req, res) => {
try {
const { userId, title, body, data } = req.body;
// Get the user's FCM token from the database
const tokenDoc = await Token.findOne({ userId });
if (!tokenDoc) {
return res.status(404).json({ error: 'User token not found' });
}
const response = await sendNotificationToDevice(tokenDoc.token, title, body, data);
res.status(200).json({ success: true, response });
} catch (error) {
console.error('Error sending notification:', error);
res.status(500).json({ error: 'Server error' });
}
});
Using the Firebase Admin SDK:
import firebase_admin
from firebase_admin import credentials, messaging
# Initialize the app with a service account
cred = credentials.Certificate('path/to/serviceAccountKey.json')
firebase_admin.initialize_app(cred)
# Send a message to a specific device
def send_notification_to_device(token, title, body, data=None):
if data is None:
data = {}
message = messaging.Message(
notification=messaging.Notification(
title=title,
body=body
),
data=data,
token=token
)
try:
response = messaging.send(message)
print('Successfully sent message:', response)
return response
except Exception as e:
print('Error sending message:', e)
raise e
# Send a message to multiple devices
def send_notification_to_devices(tokens, title, body, data=None):
if data is None:
data = {}
message = messaging.MulticastMessage(
notification=messaging.Notification(
title=title,
body=body
),
data=data,
tokens=tokens
)
try:
response = messaging.send_multicast(message)
print(f'{response.success_count} messages were sent successfully')
return response
except Exception as e:
print('Error sending messages:', e)
raise e
# Send a message to a topic
def send_notification_to_topic(topic, title, body, data=None):
if data is None:
data = {}
message = messaging.Message(
notification=messaging.Notification(
title=title,
body=body
),
data=data,
topic=topic
)
try:
response = messaging.send(message)
print('Successfully sent message to topic:', response)
return response
except Exception as e:
print('Error sending message to topic:', e)
raise e
# Example Flask API endpoint to send notification
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/send-notification', methods=['POST'])
def send_notification():
try:
data = request.json
user_id = data.get('userId')
title = data.get('title')
body = data.get('body')
custom_data = data.get('data', {})
# Get the user's FCM token from the database
# This is a placeholder - implement your database logic
token = get_user_token(user_id)
if not token:
return jsonify({'error': 'User token not found'}), 404
response = send_notification_to_device(token, title, body, custom_data)
return jsonify({'success': True, 'response': response}), 200
except Exception as e:
print('Error sending notification:', e)
return jsonify({'error': 'Server error'}), 500
def get_user_token(user_id):
# Implement your database logic to retrieve the token
# This is a placeholder
return "user_fcm_token"
if __name__ == '__main__':
app.run(debug=True)
Using the Firebase Admin SDK:
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@SpringBootApplication
@RestController
public class FcmApplication {
public static void main(String[] args) throws IOException {
// Initialize Firebase Admin SDK
FileInputStream serviceAccount = new FileInputStream("path/to/serviceAccountKey.json");
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();
FirebaseApp.initializeApp(options);
SpringApplication.run(FcmApplication.class, args);
}
// Send a message to a specific device
public String sendNotificationToDevice(String token, String title, String body, Map data) throws FirebaseMessagingException {
Notification notification = Notification.builder()
.setTitle(title)
.setBody(body)
.build();
Message message = Message.builder()
.setNotification(notification)
.putAllData(data)
.setToken(token)
.build();
String response = FirebaseMessaging.getInstance().send(message);
System.out.println("Successfully sent message: " + response);
return response;
}
// Send a message to multiple devices
public BatchResponse sendNotificationToDevices(List tokens, String title, String body, Map data) throws FirebaseMessagingException {
Notification notification = Notification.builder()
.setTitle(title)
.setBody(body)
.build();
MulticastMessage message = MulticastMessage.builder()
.setNotification(notification)
.putAllData(data)
.addAllTokens(tokens)
.build();
BatchResponse response = FirebaseMessaging.getInstance().sendMulticast(message);
System.out.println(response.getSuccessCount() + " messages were sent successfully");
return response;
}
// Send a message to a topic
public String sendNotificationToTopic(String topic, String title, String body, Map data) throws FirebaseMessagingException {
Notification notification = Notification.builder()
.setTitle(title)
.setBody(body)
.build();
Message message = Message.builder()
.setNotification(notification)
.putAllData(data)
.setTopic(topic)
.build();
String response = FirebaseMessaging.getInstance().send(message);
System.out.println("Successfully sent message to topic: " + response);
return response;
}
// Example API endpoint to send notification
@PostMapping("/api/send-notification")
public ResponseEntity sendNotification(@RequestBody NotificationRequest request) {
try {
// Get the user's FCM token from the database
// This is a placeholder - implement your database logic
String token = getUserToken(request.getUserId());
if (token == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User token not found");
}
String response = sendNotificationToDevice(token, request.getTitle(), request.getBody(), request.getData());
return ResponseEntity.ok(response);
} catch (FirebaseMessagingException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error sending notification");
}
}
private String getUserToken(String userId) {
// Implement your database logic to retrieve the token
// This is a placeholder
return "user_fcm_token";
}
// Request model
public static class NotificationRequest {
private String userId;
private String title;
private String body;
private Map data;
// Getters and setters
}
}
FCM topic messaging allows you to send a message to multiple devices that have opted in to a particular topic.
Subscribe to a topic on the client:
// Android
FirebaseMessaging.getInstance().subscribeToTopic("news")
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Log.d(TAG, "Subscribed to news topic");
} else {
Log.e(TAG, "Failed to subscribe to news topic", task.getException());
}
});
// iOS
Messaging.messaging().subscribe(toTopic: "news") { error in
if let error = error {
print("Failed to subscribe to news topic: \(error)")
} else {
print("Subscribed to news topic")
}
}
// Flutter
FirebaseMessaging.instance.subscribeToTopic('news')
.then((value) => print('Subscribed to news topic'))
.catchError((error) => print('Failed to subscribe to news topic: $error'));
// React Native
messaging()
.subscribeToTopic('news')
.then(() => console.log('Subscribed to news topic'))
.catch(error => console.log('Failed to subscribe to news topic:', error));
Send a message to a topic from the server:
// Node.js
const message = {
notification: {
title: 'Breaking News',
body: 'New article available'
},
topic: 'news'
};
admin.messaging().send(message)
.then((response) => {
console.log('Successfully sent message to topic:', response);
})
.catch((error) => {
console.error('Error sending message to topic:', error);
});
// Python
message = messaging.Message(
notification=messaging.Notification(
title='Breaking News',
body='New article available'
),
topic='news'
)
response = messaging.send(message)
print('Successfully sent message to topic:', response)
// Java
Message message = Message.builder()
.setNotification(Notification.builder()
.setTitle("Breaking News")
.setBody("New article available")
.build())
.setTopic("news")
.build();
String response = FirebaseMessaging.getInstance().send(message);
System.out.println("Successfully sent message to topic: " + response);
The easiest way to test your FCM implementation is to send a test message from the Firebase Console:
You can also test your FCM implementation using cURL commands:
curl -X POST -H "Authorization: key=YOUR_SERVER_KEY" -H "Content-Type: application/json" \
-d '{
"to": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Test Notification",
"body": "This is a test notification from cURL"
},
"data": {
"key1": "value1",
"key2": "value2"
}
}' \
https://fcm.googleapis.com/fcm/send
Postman provides a user-friendly interface for testing API calls:
https://fcm.googleapis.com/fcm/send{
"to": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Test Notification",
"body": "This is a test notification from Postman"
},
"data": {
"key1": "value1",
"key2": "value2"
}
}
Firebase Cloud Messaging provides a reliable and scalable solution for sending push notifications to your users across different platforms. By following the implementation steps and best practices outlined in this guide, you can effectively integrate FCM into your application and enhance user engagement.
Remember to:
With FCM, you can keep your users informed and engaged with timely and relevant notifications, ultimately improving user retention and satisfaction.