Today, in digital world, keeping user engaged is the key determinant of any application’s success. With push notifications, you have a direct to tell your users what’s going on in a timely fashion, with alerts and also personal messages. The rise of cross platform frameworks such as .NET Multi platform UI (.NET MAUI) is the fact that developers can build native applications across multiple platforms from a single codebase.
Firebase Cloud Messaging (FCM) is a wonderful service from Google that allows you to communicate with your users on various platforms. Leveraging the full capacity of push notifications from FCM, you can integrate FCM with .NET MAUI.
In this article I will explain how to implement push notifications in your .NET MAUI application using Firebase Cloud Messaging. In this article, let’s explore how to set your development environment, begin with FCM integration with your application and effectively handle notifications.
Understanding Firebase Cloud Messaging
Firebase Cloud Messaging is a free cross platform messaging service that allows you to reliably send notifications and messages to your users without it breaking. It supports many message types so you can have real time communication with your users’ app. Key features include:
Multi-Platform Support: It works great on Android, iOS, and web applications.
Message Targeting: You can create the messages to follow the specific devices, groups of devices or topics.
Analytics: It presents detailed delivery reports and analytics in order to measure message effectiveness.
Scalability: Created to process large amount of messages.
FCM takes care of delivering messages across multiple platforms and devices; It takes away all the complexity in message delivery. With message targeting, delivery analytics and device group messaging, it is a solid solution for the push notifications implementation.
Why Use Push Notifications?
User Engagement and Retention are imperative for user push notifications. They allow you to:
Deliver Timely Information: Intuitively, it will inform users about important events, updates or promotions instantly. Take for instance, a news app can send out breaking news alerts.
Increase User Engagement: Bring your users back with customized messages. One example is that a shopping app can tell users about their discount offers.
Enhance User Experience: Recommend value added services such as reminders, alerts and interactive messages. An example is a Calendar app that sends reminders for events.
Push notifications can help your app achieve higher effectiveness and satisfy your users better.
Setting Up a Firebase Project
- Create a New Project
Access Firebase Console:
Head to the Firebase Console.
Add a Project:
Click on Add project.
Project Name: Then enter a unique name for your project.
Analytics: Enable Or Disable Google Analytics for your project.
Create Project: Click on Create Project to continue. - Register Your App
Add Android App:
Click on the Android icon.
Package Name: Give in your app’s’s package name (e.g. com.companyname.appname).
App Nickname: Helpful for identification, optional.
SHA-1 Certificate: Optional for FCM and required for some Firebase features.
Register App: Proceed by clicking the Register App option.
Add iOS App:
Click on the iOS icon.
Bundle ID: Paste your app’s bundle identifier.
App Nickname: Optional.
App Store ID: Optional.
Register App: Click Register App. - Download Configuration Files
Android (google-services.json):
Download the google-services.json file after registering the Android app.
Put this file into your .NET MAUI project’s Platforms/Android location.
iOS (GoogleService-Info.plist):
Then, after registering the iOS app, download the GoogleService-Info.plist file.
Simply place this file in the Platforms/iOS project in Visual Studio.
You can also set its Build Action to BundleResource. - Add Firebase SDKs
For Android:
In the integration section, we’ll include the Firebase SDKs via NuGet packages.
For iOS:
As we’ll also do the same thing we’ll also add the required Firebase SDKs using NuGet packages.
Completing these steps will take your Firebase project and make it ready to conversation with your .NET MAUI application.
Integrating Firebase Cloud Messaging with .NET MAUI
Create a new .NET MAUI project or adjust your project like below.
Add Firebase Packages
Install Xamarin.Firebase.Messaging for Android:
Click on the Package Manager Console in Visual Studio menu.
Run the following command:
Install-Package Xamarin.Firebase.Messaging -Version 123.0.4
Change 123.0.4 here with the most current version at your disposal.
Install Firebase.CloudMessaging for iOS:
Run the following command:
Install-Package Firebase.CloudMessaging -Version 4.8.0
Replace 4.8.0
with the latest version available.
Include Configuration Files
Android:
Then put the google-services.json in Platforms/Android folder.
In Visual Studio right click on the google-services.json file and change its Build Action to GoogleServicesJson.
iOS:
Then, add the GoogleService-Info.plist to Platforms/iOS project.
You can also set BundleResource as Build Action.
Configuring the App for Firebase
Update AndroidManifest.xml:
Navigate to Platforms/Android on AndroidManifest.xml.
Add the following permissions inside the tag:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
Add the Firebase Messaging service inside the <application>
tag:
<application ...>
<service
android:name="com.google.firebase.messaging.FirebaseMessagingService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
Add Google Services Gradle Plugin:
Create a file named build.gradle
in the Platforms/Android
directory with the following content:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.google.gms:google-services:4.3.10'
}
}
Modify the build.gradle
file to apply the Google Services plugin:
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
iOS Configuration
Enable Push Notifications Capability:
Open Platforms/iOS/Entitlements.plist.
To enable Push Notifications go ahead and add a key aps-environment in the value of development or production in relation to your environment.
Register for Remote Notifications in AppDelegate.cs:
using Firebase.Core;
using UserNotifications;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// Configure Firebase
App.Configure();
// Request permission to display alerts and play sounds.
UNUserNotificationCenter.Current.RequestAuthorization(
UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Badge,
(granted, error) => {
if (granted)
{
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);
}
});
return base.FinishedLaunching(app, options);
}
// Handle token refresh
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
// Convert device token to string
var token = deviceToken.ToString();
// Send token to Firebase
Messaging.SharedInstance.ApnsToken = deviceToken;
}
Implement UNUserNotificationCenterDelegate:
In your AppDelegate add the IUNUserNotificationCenterDelegate interface.
Handle incoming notifications through the implemention of the delegate methods.
After you complete the integration steps the process to use Firebase Cloud Messaging with your .NET MAUI app is complete.
Implementing Push Notifications in .NET MAUI
Firebase is now integrated so now you can implement the functionality to receive and handle push notifications.
Handling Device Registration
Obtain FCM Token
Android:
Create a service class that extends FirebaseMessagingService
:
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
public override void OnNewToken(string token)
{
base.OnNewToken(token);
// TODO: Send token to your server or save it as needed
Console.WriteLine($"FCM Token: {token}");
}
}
iOS:
In AppDelegate.cs
, implement the MessagingDelegate
:
using Firebase.CloudMessaging;
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IMessagingDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// Existing code...
Messaging.SharedInstance.Delegate = this;
return base.FinishedLaunching(app, options);
}
[Export("messaging:didReceiveRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string token)
{
// TODO: Send token to your server or save it as needed
Console.WriteLine($"FCM Token: {token}");
}
}
Store or Send Token to Server
Send Token to Server:
As a responsive of this activate token, create a method that can send the token to your server via HTTP POST or anything else you prefer.
private async Task SendTokenToServer(string token)
{
var client = new HttpClient();
var content = new StringContent($"{{\"token\":\"{token}\"}}", Encoding.UTF8, "application/json");
await client.PostAsync("https://yourserver.com/api/register-token", content);
}
Store Token Locally
Keep the token in use secure storage mechanism
await SecureStorage.SetAsync("fcm_token", token);
Monitor Token Refresh
Android:
In MyFirebaseMessagingService is where you can put your token refresh.
iOS:
So the method for handling token refresh in AppDelegate is the DidReceiveRegistrationToken method.
Sending Notifications from the Firebase Console
- Click to Firebase Console.
Head over to your Firebase project, and from the side menu pick Cloud Messaging. - Compose a New Notification
Click on “Send your first message”:
Click on “New notification” if you’ve already sent messages.
Enter Notification Details:
Title: e.g., “Welcome to Our App!”
Text: For example, they could say “We thank you for installing our app, stay tuned for updates.” - Target Your App
Select App:
Under Target choose your registered app.
Target Specific Devices (Optional):
By Topic, User Segment or Device Group you can focus your targeting. - Send the Message
Schedule Delivery:
For now or a future date and time.
Send:
Select ‘Review’ then ‘Publish’ and the notification will be sent.
It allows you to test the notification reception on your app without setting up a server.
Handling Received Notifications in the App
Create a Custom Messaging Service
Android:
Continue using MyFirebaseMessagingService
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
// TODO: Handle the message
SendNotification(message.GetNotification().Title, message.GetNotification().Body);
}
private void SendNotification(string title, string messageBody)
{
var notificationBuilder = new NotificationCompat.Builder(this, "default")
.SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
.SetContentTitle(title)
.SetContentText(messageBody)
.SetAutoCancel(true);
var notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(0, notificationBuilder.Build());
}
Add Notification Channel for Android Oreo and Above:
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channel = new NotificationChannel("default", "Default Channel", NotificationImportance.Default)
{
Description = "Firebase Notifications"
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
}
iOS:
Implement methods in UNUserNotificationCenterDelegate
like below:
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
// Show the notification even if the app is in the foreground
completionHandler(UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Sound);
}
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
// TODO: Handle notification tap action
var userInfo = response.Notification.Request.Content.UserInfo;
// Process userInfo as needed
completionHandler();
}
Handle Notification Clicks
Android
Launch an activity when you tap that notification by defining a pending intent that leads to the activity.
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
notificationBuilder.SetContentIntent(pendingIntent);
iOS
Steps to Handle Notification Clicks in iOS:
- In your AppDelegate class implement the IUNUserNotificationCenterDelegate interface.
- And then, set your AppDelegate instance to the UNUserNotificationCenter.Current.Delegate.
- In order to handle the notification click event, we override the DidReceiveNotificationResponse method.
- If you want to extract custom data from the notification payload do so.
- Allows you to navigate to specific pages in your app.
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// Configure Firebase
App.Configure();
// Set Messaging Delegate
Messaging.SharedInstance.Delegate = this;
// Request permission to display notifications
UNUserNotificationCenter.Current.RequestAuthorization(
UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Badge,
(granted, error) =>
{
if (granted)
{
// Set the notification center delegate
UNUserNotificationCenter.Current.Delegate = this;
// Register for remote notifications
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);
}
else
{
Console.WriteLine("Permission not granted: " + error?.LocalizedDescription);
}
});
return base.FinishedLaunching(app, options);
}
// Called when a notification is delivered to a foreground app.
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
// Show the notification even if the app is in the foreground
completionHandler(UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Badge);
}
// Called when the user interacts with the notification (e.g., taps on it).
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
// Handle the notification action
var userInfo = response.Notification.Request.Content.UserInfo;
// Extract custom data from the notification payload
if (userInfo != null && userInfo.ContainsKey(new NSString("customKey")))
{
var customValue = userInfo[new NSString("customKey")].ToString();
// TODO: Navigate to a specific page or perform an action using customValue
HandleNotificationAction(customValue);
}
else
{
// TODO: Handle the default action
HandleNotificationAction(null);
}
// Must be called when finished
completionHandler();
}
// Handle FCM token refresh
[Export("messaging:didReceiveRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
Console.WriteLine($"FCM Token: {fcmToken}");
// TODO: If necessary send token to your server
}
// Handle successful registration for remote notifications
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Messaging.SharedInstance.ApnsToken = deviceToken;
}
// Handle failure to register for remote notifications
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
Console.WriteLine($"Failed to register for remote notifications: {error.LocalizedDescription}");
}
private void HandleNotificationAction(string customValue)
{
// Ensure this is run on the main thread
Device.BeginInvokeOnMainThread(() =>
{
// Access the current application
var app = Application.Current;
// Navigate to a specific page based on customValue
if (app.MainPage is NavigationPage navigationPage)
{
if (!string.IsNullOrEmpty(customValue))
{
// Navigate to the page with custom data
navigationPage.PushAsync(new NotificationDetailPage(customValue));
}
else
{
// Navigate to a default page
navigationPage.PushAsync(new MainPage());
}
}
else
{
// If MainPage is not a NavigationPage, set it
app.MainPage = new NavigationPage(new MainPage());
}
});
}
}
Notification Payload Customization
Notification Messages vs. Data Messages
Data Messages:
They contain custom key value pairs defined by the developer.
Even when the app is in the foreground, it’s handled by the app.
Notification Messages:
It contains predefined keys such as title and body.
If the app is not on foreground, is displayed automatically on the system tray.
Crafting Custom Payloads
Data message example
{
"to": "<FCM_TOKEN>",
"data": {
"title": "New Feature!",
"body": "Check out the latest update in our app.",
"url": "https://yourapp.com/feature"
}
}
Sending via HTTP POST Request
Endpoint: https://fcm.googleapis.com/fcm/send
Headers:
Content-Type: application/json
Authorization: key=<SERVER_KEY>
Create a request using tools like Postman or write server side script to send the request.
Handling in the App
Android
In OnMessageReceived
method, access data payload:
if (message.Data.Count > 0)
{
var title = message.Data["title"];
var body = message.Data["body"];
var url = message.Data["url"];
// Display notification or update UI
}
iOS
In DidReceiveRemoteNotification
method, access data:
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
if (userInfo != null)
{
var data = userInfo["data"] as NSDictionary;
var title = data["title"].ToString();
var body = data["body"].ToString();
var url = data["url"].ToString();
// Handle data
}
}
Handling Background Notifications
Background Processing
Android
- The app receives data messages even when it’s in the background.
- Make sure your FirebaseMessagingService is setup correctly to recieve background messages.
iOS
- To allow processing of data messages in the background, it has special requirements.
- Enable Remote notifications background mode:
- Go into your project’s Capabilities, select Background Modes, and Remote notifications.
iOS Specifics
Set content-available
Flag
Include “content-available”: Add a 1 in your payload to indicate a silent notification.
{
"to": "<FCM_TOKEN>",
"content_available": true,
"priority": "high",
"data": {
"title": "Background Update",
"body": "Data has been updated.",
"key": "value"
}
}
Implement DidReceiveRemoteNotification
In AppDelegate.cs
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
// Process data message
completionHandler(UIBackgroundFetchResult.NewData);
}
Using Server-Side APIs
Alternative: Using Third-Party Libraries
If you want to use a library that provides more abstraction of this complexity, then you’ll be using third party NuGet packages such as FirebaseAdmin (unofficial). Install it using:
Install-Package FirebaseAdmin
Example
using FirebaseAdmin;
using FirebaseAdmin.Messaging;
using Google.Apis.Auth.OAuth2;
public async Task SendNotificationWithFirebaseAdminAsync(string fcmToken)
{
if (FirebaseApp.DefaultInstance == null)
{
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromFile("path/to/serviceAccountKey.json"),
});
}
var message = new Message()
{
Token = fcmToken,
Notification = new Notification()
{
Title = "Hello from FirebaseAdmin",
Body = "This is a test notification."
}
};
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
Console.WriteLine("Successfully sent message: " + response);
}
Sending Notifications from C# Using Firebase Cloud Messaging
Installing Required NuGet Packages
Install-Package Google.Apis.Auth
Install-Package Google.Apis.FirebaseCloudMessaging.v1
Install-Package Newtonsoft.Json
using Google.Apis.Auth.OAuth2;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
public class FcmSender
{
private async Task<string> GetAccessTokenAsync()
{
string[] scopes = new string[] { "https://www.googleapis.com/auth/firebase.messaging" };
GoogleCredential credential;
using (var stream = new FileStream("path/to/serviceAccountKey.json", FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(scopes);
}
var accessToken = await credential.UnderlyingCredential.GetAccessTokenForRequestAsync();
return accessToken;
}
public async Task SendPushNotificationAsync(string fcmToken)
{
string accessToken = await GetAccessTokenAsync();
var message = new
{
message = new
{
token = fcmToken,
notification = new
{
title = "Hello from C#",
body = "This is a push notification sent from a C# backend."
}
// You can include "data" payload here if needed
}
};
string jsonMessage = JsonConvert.SerializeObject(message);
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
var request = new HttpRequestMessage(HttpMethod.Post,
"https://fcm.googleapis.com/v1/projects/your-project-id/messages:send");
request.Content = new StringContent(jsonMessage, Encoding.UTF8, "application/json");
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Notification sent successfully.");
}
else
{
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Error sending notification: {response.StatusCode}\n{responseBody}");
}
}
}
}
Sending http request
string userFcmToken = "user_device_fcm_token";
await SendPushNotificationAsync(userFcmToken);
Common Issues and Solutions
Token Retrieval Failures:
- Make sure that the network is working, and Firebase can be initialized.
- In token retrieval methods check for exceptions.
Notifications Not Received:
- Make sure that you app have the proper permission in it.
- We check to see if the device is registered and the token if valid.
Payload Format Errors:
- Use online or stand alone JSON payload validate tools.
- Inputs can be formatted correctly to keys and values.
Conclusion
Push notifications integrations using Firebase Cloud Messaging in your .NET MAUI application increase user engagement and offer real time communication. Using this article as guidance, you’ll set up your development environment, integrate FCM with your app, and add notification handling.
Mastering these concepts empowers you deliver timely, and personalized content to your users, thereby enhancing the user experience. Second, if working with Firebase, you may want to dive deeper on their advanced features like topic messaging, conditional targeting, and analytics to localize communication strategies in your app.