Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) is a cross-platform messaging service that provides a reliable and battery-efficient connection between your server and devices and lets you reliably deliver messages at no cost. It handles the sending, routing, and queueing of messages between server applications and mobile client apps. FCM acts as an intermediary between message senders and clients. 

How does it work?
An FCM implementation includes two main components for sending and receiving:
  1. An environment such as Cloud Functions for Firebase or an app server on which to build, target and send messages.
  2. An iOS or Android client app that receives messages.
 

 

Setting Up Firebase Cloud Messaging

First of all, You must create a new project (or import an existing project) via the Firebase Console before you can use FCM services in your app. Use the following steps to create a Firebase Cloud Messaging project for your app:

  1. Sign into the Firebase Console with your Google account (i.e., your Gmail address) and click CREATE NEW PROJECT:
 
     2In the Create a project dialog, enter the name of your project and click CREATE PROJECT. In the following example, a new project called ChatAPP is created.
 
 
 
       3In the Firebase Console Overview, click Add Firebase to your Android app:
 
 
 
similarly you can add Firebase to your iOS app by clicking on Add Firebase to your iOS app in the same project.
 
      4In the next screen, enter the package name of your app. This value must match the package name of your Android app. You can also enter the App nickname in the App nickname field.
 
     5Click ADD APP
 
 
 
      6.  Download google-services.json file.
 

Remote Notifications with Firebase Cloud Messaging

 
This blog will provides a step-by-step explanation of how to use Firebase Cloud Messaging to implement remote notifications (also called push notifications) in a Xamarin.Android application. But before you can proceed with this, you must acquire the necessary credentials to use Google's FCM server as explained above.

Start an App Project

To begin with, create  a new xamarin.Forms project. After the new app is created, the next step is to set the package name and install several nuget packages that will be used for communication with FCM.

Set the Package Name

In Firebase Cloud Messaging, you specified a package name for the FCM-enabled app. This package name also serves as the application ID that is associated with the API key. Configure the app to use this package name:
  1. Open the properties of your project.
  2. In the Android Manifest page, set the package name.

Add the Xamarin Google Play Services Base Package

  1. In Visual Studio, right-click References > Manage NuGet Packages ....
  2. Click the Browse tab and search for Xamarin.GooglePlayServices.Base.
  3. Install this package into the ChatAPP project:
If you get an error during installation of the NuGet, close your project, open it again, and retry the NuGet installation.

Add the Xamarin Firebase Messaging Package

To receive messages from FCM, the Xamarin Firebase - Messaging package must be added to the app project. Without this package, an Android application cannot receive messages from FCM servers.
  1. In Visual Studio, right-click References > Manage NuGet Packages ....
  2. Check Include prerelease and search for Xamarin.Firebase.Messaging.
  3. Install this package into your project:
 
 
Next, edit MainActivity.cs and add the following using statements:
using Firebase.Messaging;
using Firebase.Iid;
using Android.Util;

Add the Google Services JSON File

  1. Copy google-services.json to the project folder.
  2. Add google-services.json to the app project.
  3. Select google-services.json in the Solution Explorer window.
  4. In the Properties pane, set the Build Action to GoogleServicesJson:
 
 
Now replace the OnCreate method with the following code: 
protected override void OnCreate (Bundle bundle)
{
   base.OnCreate (bundle);
   global::Xamarin.Forms.Forms.Init (this, bundle);
   FirebaseApp.InitializeApp(this);
   Task.Run(async () =>
     {
        var instanceId = FirebaseInstanceId.Instance;
        Console.WriteLine("TAG", "{0} {1}", instanceId?.Token?.ToString(), instanceId.GetToken(GetString(Resource.String.gcm_defaultSenderId), Firebase.Messaging.FirebaseMessaging.InstanceIdScope));
     });
   if (Intent.Extras != null)
    {
        foreach (var key in Intent.Extras.KeySet())
        {
           var value = Intent.Extras.GetString(key);
           Console.WriteLine("tag", "Key: {0} Value: {1}", key, value);
         }
    }
 LoadApplication (new ChatApp.App ());
}
 
Declare the Receiver in the Android Manifest
Edit AndroidManifest.xml and add some permission required for sending and receiving notification and also insert the following <receiver> elements into the <application> section:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />  
<receiver 
    android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" 
    android:exported="false" />
<receiver 
    android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" 
    android:exported="true" 
    android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

Implement the Firebase Instance ID Service

FirebaseInstanceIdService performs the following steps:

  1. Uses the Instance ID API to generate security tokens that authorize the client app to access FCM and the app server. In return, the app gets back a registration token from FCM.
  2. Forwards the registration token to the app server if the app server requires it.
Add a new file called MyFirebaseIIDService.cs and replace its template code with the following: 
using System;
using Android.App;
using Firebase.Iid;
using Android.Util;

namespace FCMClient
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseIIDService : FirebaseInstanceIdService
    {
        const string TAG = "MyFirebaseIIDService";
        public override void OnTokenRefresh()
        {
            var refreshedToken = FirebaseInstanceId.Instance.Token;
            Log.Debug(TAG, "Refreshed token: " + refreshedToken);
            SendRegistrationToServer(refreshedToken);
        }
        void SendRegistrationToServer(string token)
        {
            // Add custom implementation, as needed.
        }
    }
}
This service implements an OnTokenRefresh method that is invoked when the registration token is initially created or changed. When OnTokenRefresh runs, it retrieves the latest token from the FirebaseInstanceId.Instance.Token property .

Implement FirebaseMessagingService

The FirebaseMessagingService service includes an OnMessageReceived method that you write to handle incoming messages. 
Add a new file called MyFirebaseMessagingService.cs and replace its template code with the following:
using System;
using Android.App;
using Android.Content;
using Android.Media;
using Android.Util;
using Firebase.Messaging;

namespace FCMClient
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class MyFirebaseMessagingService : FirebaseMessagingService
    {
        const string TAG = "MyFirebaseMsgService";
        public override void OnMessageReceived(RemoteMessage message)
        {
            SendNotification (message.GetNotification().Body);
        }
    }
}

Add a Local Notifications Sender

Add the following method to MyFirebaseMessagingService:
void SendNotification(string messageBody)
{
    var intent = new Intent(this, typeof(MainActivity));
    intent.AddFlags(ActivityFlags.ClearTop);
    var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);

    var notificationBuilder = new Notification.Builder(this)
        .SetContentTitle("FCM Message")
        .SetContentText(messageBody)
        .SetAutoCancel(true)
        .SetContentIntent(pendingIntent);

    var notificationManager = NotificationManager.FromContext(this);
    notificationManager.Notify(0, notificationBuilder.Build());
}
 
Xamarin.iOS Requirements:-

Add Firebase to your app
  1. Click Add Firebase to your iOS app and follow the setup steps in the project you created for Android.
  2. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project.
  3. At the end, you'll download a GoogleService-Info.plist file.
 

Configure Cloud Messaging in your app

Once you have your GoogleService-Info.plist file downloaded in your computer, do the following steps in Xamarin Studio:
  1. Add GoogleService-Info.plist file to your app project.
  2. Set GoogleService-Info.plist build action behaviour to Bundle Resource by Right clicking/Build Action.
  3. Add Firebase Cloud Messaging for iOS nuget package in ios project.

Create APN SSL Certificate

you'll need to create an APNs SSL Certificate, then upload it to Firebase and finally register the app for remote notifications.
 
 
 

Register for remote notifications

Either at startup, or at the desired point in your application flow, register your app for remote notifications. Call RegisterForRemoteNotifications method as shown in FinishedLaunching method:
// Register your app for remote notifications.
if (UIDevice.CurrentDevice.CheckSystemVersion (10, 0)) {
    // iOS 10 or later
    var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
    UNUserNotificationCenter.Current.RequestAuthorization (authOptions, (granted, error) => {
        Console.WriteLine (granted);
    });

    UNUserNotificationCenter.Current.Delegate = this;

    Messaging.SharedInstance.RemoteMessageDelegate = this;
} else {
    // iOS 9 or before
    var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
    var settings = UIUserNotificationSettings.GetSettingsForTypes (allNotificationTypes, null);
    UIApplication.SharedApplication.RegisterUserNotificationSettings (settings);
}
 Firebase.Analytics.App.Configure();
UIApplication.SharedApplication.RegisterForRemoteNotifications ();
 Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
            {
                var newToken = Firebase.InstanceID.InstanceId.SharedInstance.Token;
                // if you want to send notification per user, use this token
                System.Diagnostics.Debug.WriteLine(newToken);
            });

Receive messages

Firebase notifications behave differently depending on the foreground/background state of the receiving app.

Receive messages through FCM

To receive or send messages through FCM, you'll need to connect to the FCM service. Connect when your application becomes active and whenever a new registration token is available.
Messaging.SharedInstance.Connect (error => {
    if (error != null) {
        // Handle if something went wrong while connecting
    } else {
        // Let the user know that connection was successful
    }
});
When your app goes into the background, disconnect from FCM:
public override void DidEnterBackground (UIApplication application)
{
    Messaging.SharedInstance.Disconnect ();
    Console.WriteLine ("Disconnected from FCM");
}

Handling messages

For devices running iOS 9 and below, override AppDelegate's DidReceiveRemoteNotification method to handle notifications received when the client app is in the foreground, and all data messages that are sent to the client. The message is a dictionary of keys and values:
public override void DidReceiveRemoteNotification (UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
    // Do your magic to handle the notification data
    System.Console.WriteLine (userInfo);
}
For iOS 10 devices and above, implement IUNUserNotificationCenterDelegate interface and override WillPresentNotification method to handle notifications received when the client app is in the foreground:
// To receive notifications in foreground on iOS 10 devices.
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
    // Do your magic to handle the notification data
    System.Console.WriteLine(notification.Request.Content.UserInfo);
}

public void ApplicationReceivedRemoteMessage(RemoteMessage remoteMessage)
{
    Console.WriteLine(remoteMessage.AppData);
}

How to Send a message to a single device.

Access the registration token

To send a message to a specific device, you need to know that device's registration token.
To retrieve the current token, you will need to call the following property. This property returns null if the token has not yet been generated:
var token = InstanceId.SharedInstance.Token;

Send a message

  1. Install and run the app on the target device. You'll need to accept the request for permission to receive remote notifications.
  2. Make sure the app is in the background on the device.
  3. Open the Notifications tab of the Firebase console and select New Message.
  4. Enter the message text.
  5. Select Single Device for the message target.
  6. In the field labeled FCM Registration Token, enter the registration token you obtained in a previous section of this guide.
 
Reference- 
https://developer.xamarin.com/guides/android/application_fundamentals/notifications/firebase-cloud-messaging/
https://components.xamarin.com/view/firebaseioscloudmessaging