c# FCM

Firebase Cloud Messaging by Using C#

Matt 2020/05/04 11:44:11
16404

What is FCM ?

FCM is now the most popular Push Notifications technique around the world. It can help us to deliver information to devices (including Android and iOS, or web).

img "push notifications from mobile devices"

What exactly is that ? Firebase Cloud Messaging, aka FCM is a cross-platform messaging solution that lets you reliably send messages at no cost.

An FCM implementation includes two main components for sending and receiving:

  • A trusted environment such as Cloud Functions for Firebase or an app server on which to build, target, and send messages.
  • An iOS, Android, or web (JavaScript) client app that receives messages via the corresponding platform-specific transport service.

-SDK: FirebaseAdmin

Our goal is sending messages through the SDK FirebaseAdmin, and it stands on Google.Apis SDK. We still can do it by calling the RESTful Apis, but that's too paintful.

img "FirebaseAdmin on Nuget"

Workshop

-register device (client side)

getIdToken from devices.

Android code sample:

FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getIdToken(true).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
    public void onComplete(@NonNull Task<GetTokenResult> task) {
        if (task.isSuccessful()) {
            String idToken = task.getResult().getToken();
            // Send token to your backend via HTTPS
            // ...
        } else {
            // Handle error -> task.getException();
        }
    }
});

A valid IdToken should look like something shown blow,

xX8ybejXXxX:APA91bFflwiIKCKp8pFeE6k5rYhFPxkOSwEqyLJvNwmKWB2XxxXx7XxX3gHmfW3RcLvQw0jU3TTNsggX0SPL01IT23lxXxXX1liY2KNc4k2By9wm3iVvXdcNdCjE3uLjGSe_5XXxXxXx

or

xXxXpjVwkkHYlljx_5DFQy:APA91xXXXXGkaUofGg7F_M9pokgHlJdXXXlcl60Xy76lXlrD3XxX2qdpQctG3oXxxxx62xX3-uycwCQsbF7Fs56I99XXX3389HJuxXxxXn1XX2rC9Tw28ZkyP-u_4X94Hb9XXX0v5s47

the length will NOT a fixed number, it should be 152 Or 163. After we get the IdToken, we used to store it to database, or some other store system we could grab token when we need it.

In some cases, we might subscribe the client to a Topic.

FirebaseMessaging.getInstance().subscribeToTopic("highbonus")
    .addOnCompleteListener(new OnCompleteListener<Void>() {
        @Override
        public void onComplete(@NonNull Task<Void> task) {
            String msg = getString(R.string.msg_subscribed);
            if (!task.isSuccessful()) {
                msg = getString(R.string.msg_subscribe_failed);
            }
            Log.d(TAG, msg);
            Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
        }
    });

We will introduce Topic soon.

-FirebaseApp Credential

Before sending push notification messages, an app instance (FirebaseApp) should be authorized first. How to do it ?

Let's look some codes,

    // the easist way to create an instance without giving appName, it will be signed a [Default] name.
    var app = Credential.Create(new AppOptions
    {
        Credential = GoogleCredential.FromFile("path of JSON credential data")
    });


    // I suggest to create by specifying a name for instance while we have multiple projects to setup.
    var app = FirebaseApp.Create(new AppOptions
    {
        ProjectId = "an indicator",
        Credential = GoogleCredential.FromFile("path of JSON credential data")
    }, "an app name");
	

And what is JSON credential data ? And where is it located ? In Firebase Console, Service Account Tab.

img "firebase project console"

We will get a json file named "your_project_name-firebase-adminsdk-blablabla.json" while clicked the generate button.

A JSON credential data will look like content shown blow,

{
    "type": "service_account",
    "project_id": "your_project_name",
    "private_key_id": "your_key_id",
    "private_key": "-----BEGIN PRIVATE KEY-----\nblablabla\n-----END PRIVATE KEY-----\n",
    "client_email": "system_gave_email",
    "client_id": "your_client_id",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-blablabla-android.iam.gserviceaccount.com"
}

Yes, FirebaseApp Credential is based on Google OAuth2 system.

-sending message (server side)

FirebaseAdmin provides some scenarios to send push messages.

Check them out.

-single push, deliver a notification to a device in one http request

This is basic way to send push notifications. And, we need to using FirebaseAdmin.Messaging; Namespace first, before start it.

        var message = new Message
        {
            // payload data, some additional information for device
            Data = new Dictionary<string, string>
            {
                { "additional_data", "a string" },
                { "another_data", "another string" },
                ......
            },
			
            // the notification itself
            Notification = new Notification
            {
                Title = "a title",
                Body = "a body",
                ImageUrl = "https://a_image_url"
            },
            Token = "a valid token",
        };
        var firebaseMessagingInstance = FirebaseMessaging.GetMessaging(app);
        var result = await firebaseMessagingInstance.SendAsync(message).ConfigureAwait(false);

When notification delivered successfully, a FCM MessageId will be returned, if not, we will get an error message.

A valid FCM MessageId looked like

projects/your_project_name/messages/10003100043000730

or

projects/your_project_name/messages/0:1000300033800070%cblablablablablac

for that, we can follow the rule to check if a notification sent to device successfully.

-multiple push, deliver notifications to devices in one http request

What if, there're many messages to deliver to many devices, can we do that ? Sure is.

This kind of scenario might use a lot, it's a significantly more efficient way to send multiple messages to multiple devices.

Let's look at codes,

        var messages = new List<Message>();
        foreach (var row in [some datas])
        {
            messages.Add(new Message
            {
                Data = new Dictionary<string, string>
                {
                    { "additional_data", "a string" },
                    { "another_data", "another string" },
                    ......
                },
                Notification = new Notification
                {
                    Title = "a title",
                    Body = "a body"
                },
                Token = "a valid token"
            });
        }
        var firebaseMessagingInstance = FirebaseMessaging.GetMessaging(app);
        var batchResponse = await firebaseMessagingInstance.SendAllAsync(messages).ConfigureAwait(false);

Notice that, because of some limits, every request, we can just only put 100 messages into SendAllAsync() to deliver.

Another, after SendAllAsync() fired, we will get BatchResponse, that includes SuccessCount, FailureCount, and MessageId of each messages.

-multicast push, deliver one notification to lots of devices in one http request

There is still another sending scenario, that allow us to send a message to a big mass devices.

Let's take a look at it.

        // Create a list containing up to 100 registration tokens.
        // These registration tokens come from the client FCM SDKs.
        var registrationTokens = new List<string>
        {
            "YOUR_REGISTRATION_TOKEN_1",
            "YOUR_REGISTRATION_TOKEN_2",
            ......
            "YOUR_REGISTRATION_TOKEN_n",
        };
        var message = new MulticastMessage
        {
            Data = new Dictionary<string, string>
            {
                { "additional_data", "a string" },
                { "another_data", "another string" },
                ......
            },
            Notification = new Notification {
                Title = "a title",
                Body = "a body"
            },
            Tokens = registrationTokens,
        };
        var firebaseMessagingInstance = FirebaseMessaging.GetMessaging(app);
        var batchResponse = await firebaseMessagingInstance.SendMulticastAsync(message).ConfigureAwait(false);
		

After SendMulticastAsync() fired, we will still get BatchResponse.

-topic push, deliver one notification to lots of devices in one http request

There is another sending scenario, that allow us to send a message to a big mass devices.

Let's look at the codes,

        // The topic name can be optionally prefixed with "/topics/".
        var topic = "highbonus";
        var message = new Message
        {
            Data = new Dictionary<string, string>
            {
                { "additional data", "a string" },
                { "another data", "another string" },
                ......
            },
            Notification = new Notification
            {
                Title = "a title",
                Body = "a body"
            },
            Topic = topic,
        };
        var firebaseMessagingInstance = FirebaseMessaging.GetMessaging(app);
        var resp = await firebaseMessagingInstance.SendAsync(message).ConfigureAwait(false);
		

When notification delivered successfully, a FCM MessageId will be returned, if not, we will get an error message.

-condition push, deliver one notification to lots of devices in one http request

There is a derived sending method familiar to topic.

Why is that ? We can set some conditions to message.

Let's look at codes,

        var condition = "'highbonus' in topics || 'watermarks' in topics";
        var message = new Message
        {
            Data = new Dictionary<string, string>
            {
                { "additional data", "a string" },
                { "another data", "another string" },
                ......
            },
            Notification = new Notification
            {
                Title = "a title",
                Body = "a body"
            },
            Condition = condition
        };
        var firebaseMessagingInstance = FirebaseMessaging.GetMessaging(app);
        var resp = await firebaseMessagingInstance.SendAsync(message).ConfigureAwait(false);

When notification delivered successfully, a FCM MessageId will be returned, if not, we will get an error message.

There are more useful functions to help us to deal with push notifications. If you are intresting in it, please read this.

Conclusion

Push notifications are a great marketing tool for anyone with a mobile app because they help us stay in contact with our clients.

But we need to make sure to use this strategy carefully.


References:

REST Resource: projects.messages

FCM Server Protocols

FCM Architectural Overview

Firebase Admin .NET SDK

Send push to Android by C# using FCM

FcmSharp

C# 後端控制FCM推播

Matt
Matt
2020/07/27 00:34:43

In some cases, there might be an exception thrown from FCM,

if we needed to handle that respond from FCM,

 

string resp;

try

{

resp = await firebaseMessagingInstance.SendAsync(message).ConfigureAwait(false);

}

catch (Exception ex)

{

resp = $"{ex.Message}\n{ex?.InnerException?.Message}";

}

 

using try catch will be more safer to grab response.