Azure Push Notifications (Xamarin.Forms)

In this blog, push notifications is added to all the projects that were created from the Xamarin.Forms quick start. This means whenever a new record is inserted, a push notification is sent to all the cross-platform clients. A push notification is used to deliver information, such as a message, from a backend system to an application on a mobile device in order to increase application engagement and usage. The notification can be sent at anytime, even when the user is not actively using the targeted application. 

How to Add Azure push notification in your Xamarin.forms app .
 

Prerequisites

You will require an Apple Developer Program membership and an iOS device for iOS because iOS simulator does not support push notifications.
 

Steps to configure a notification hub

The Azure App Service uses Azure Notification Hubs to send pushes to targeted mobile application , so you will be configuring a notification hub for your mobile app.
  1. Open the Azure portal, go to App Services, and then click on your app back end. Under Settings, click Push option.
  2. Click Connect to add a notification hub resource to your application. You can either connect to an existing one or create a new one.

  1. Now select your notification hub and go to Access Policies, copy the connection string from the Access Policies. Later we use it in the code. 

Now you have done with the connection of the notification hub from your app's back end. Later you will configure it to communicate with the each PNS. 
 

Steps to configure and run the Xamarin.Forms Droid project 

This section is about how you enable push notifiaction for Xamarin.Form Droid project
 

Steps to enable Firebase Cloud Messaging (FCM)

  1. Open Firebase console, Sign in. You can either create a new project or choosing the existing one.
  2. After you have successfully created the project, click Add Firebase to your Android app and follow the instructions mentioned below.
  3. In the Firebase console, click Project Settings.
  4. In Project Setting, click on the Cloud Messaging tab, and copy Server key and Sender ID. These will be required later to configure the notification hub access policy, and notification handler in your app.

Steps to configure the Apps back end to send push requests using FCM

  1. In the Azure portal, click Browse All > App Services, and select your Mobile Apps back end. click Settings, click App Service Push, and then select your notification hub.
  2. Move to Google (GCM), enter the server key you copied from the Firebase console, and then press Save.
Your service is now configured to work with FCM.

Steps to add push notifications to the Android project

  1. In the Xamarin.Forms Droid project, right-click on the Components folder, and select Get More Components.... Then search for the Google Cloud Messaging Client component and add it into your project. 
  2. Open MainActivity.cs, add the following code.
    protected override void OnCreate (Bundle bundle)
     {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar; 
    
        base.OnCreate (bundle);
       // Set our view from the "main" layout resource
       //SetContentView(Resource.Layout.Main);
    
       // Initialize our Gcm Service Hub
       SampleGcmService.Initialize(this);
    
       // Register for GCM
       SampleGcmService.Register(this);
       global::Xamarin.Forms.Forms.Init (this, bundle);
       LoadApplication (new App());
    }
  3. Add a class file to the Droid project named GcmService.cs
     using Android.App;
     using Android.Content;
     using Android.Media;
     using Android.Support.V4.App;
     using Android.Util;
     using Gcm.Client;
     using Microsoft.WindowsAzure.MobileServices;
     using Newtonsoft.Json.Linq;
     using System;
     using System.Collections.Generic;
     using System.Diagnostics;
     using System.Text;
  4. Add the following permission requests at the top of the file, after the using statements and before the namespace declaration.
    [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
     [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
     [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
     [assembly: UsesPermission(Name = "android.permission.INTERNET")]
     [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
     //GET_ACCOUNTS is only needed for android versions 4.0.3 and below
     [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS") 
  5. Add the following class definition to the namespace.
    [BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
    [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })]
    [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })]
    [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })]
    public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase<GcmService>
    {
        public static string[] SENDER_IDS = new string[] { "<PROJECT_NUMBER>" };
    }
    Note:
    Replace <PROJECT_NUMBER> with your project number that you copied earlier.
  6. Replace the empty GcmService class file with the code below, which uses the new broadcast receiver:
    [Service] //Must use the service tag
     public class SampleGcmService : GcmServiceBase
     {
        static NotificationHub hub;
    
        public const string ListenConnectionString = "your-connection-string";
        public const string NotificationHubName = "NotificationHubName";
    
        public static void Initialize(Context context)
        {
            // Call this from our main activity
               
            hub = new NotificationHub(NotificationHubName, ListenConnectionString, context);
        }
    
        public static void Register(Context Context)
        {
            GcmClient.CheckDevice(Context);
            GcmClient.CheckManifest(Context);
    
            // Makes this easier to call from our Activity
            Toast.MakeText(Context, "GCM Register", ToastLength.Long).Show();
            GcmClient.Register(Context, SampleGcmBroadcastReceiver.SENDER_IDS);
        }
    
        public SampleGcmService() : base(SampleGcmBroadcastReceiver.SENDER_IDS)
        {
    
        }
     }
  7. Add the following code to the GcmService class. This overrides the OnRegistered event handler and implements a Register method.
    protected override void OnRegistered(Context context, string registrationId)
     {
         //Receive registration Id for sending GCM Push Notifications to
         if (hub != null)
             hub.Register(registrationId, "Sports");
         Toast.MakeText(context, "OnRegistered", ToastLength.Long).Show();
     }
    
     public static void Register(Context Context)
     {
         GcmClient.CheckDevice(Context);
         GcmClient.CheckManifest(Context);
    
         // Makes this easier to call from our Activity
         Toast.MakeText(Context, "GCM Register", ToastLength.Long).Show();
         GcmClient.Register(Context, SampleGcmBroadcastReceiver.SENDER_IDS);
     }
  8. Add the following code that implements OnMessage:
    protected override void OnMessage(Context context, Intent intent)
    {
         var msg = new StringBuilder();
    
         if (intent != null && intent.Extras != null)
         {
              foreach (var key in intent.Extras.KeySet())
                  msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
          }
    
          //Store the message
          var prefs = GetSharedPreferences(context.PackageName, FileCreationMode.Private);
          var edit = prefs.Edit();
          edit.PutString("last_msg", msg.ToString());
          edit.Commit();
    
          string message = intent.Extras.GetString("message");
          if (!string.IsNullOrEmpty(message))
          {
              CreateNotification("New todo item!", "Todo item: " + message);
              return;
          }
    
          string msg2 = intent.Extras.GetString("msg");
          if (!string.IsNullOrEmpty(msg2))
          {
             CreateNotification("New hub message!", msg2);
             return;
          }
    
          CreateNotification("Unknown message details", msg.ToString());
    }
    
    void CreateNotification(string title, string desc)
    {
          //Create notification
          var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
    
          //Create an intent to show ui
          var uiIntent = new Intent(this, typeof(MainActivity));
    
          //Use Notification Builder
          NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    
          //Create the notification
          //we use the pending intent, passing our ui intent over which will get called
          //when the notification is tapped.
          var notification = builder.SetContentIntent(PendingIntent.GetActivity(this, 0, uiIntent, 0))
            .SetSmallIcon(Android.Resource.Drawable.SymActionEmail)
            .SetTicker(title)
            .SetContentTitle(title)
            .SetContentText(desc)
    
             //Set the notification sound
             .SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
    
             //Auto cancel will remove the notification once the user touches it
             .SetAutoCancel(true).Build();
    
             //Show the notification
             notificationManager.Notify(1, notification);
     }
    This handles incoming notifications  from the PNS and sends them to the notification manager to be displayed.
  9. Also add the following code to GcmServiceBase which implement the OnUnregistered and OnError handler methods, which you can do as follows:
    protected override void OnUnRegistered(Context context, string registrationId)
    {
        Log.Error("PushHandlerBroadcastReceiver", "Unregistered RegisterationId : " + registrationId);
    }
    
    protected override void OnError(Context context, string errorId)
    {
        Log.Error("PushHandlerBroadcastReceiver", "GCM Error: " + errorId);
    }
Now, you are set to test push notifications in the app running on Android emulator or on Android device.

Steps to Test push notifications in your Android app

The first two steps are required only when you're running your app on the android emulator.
  1. Make sure Google API's are set as a target on the virtual device on which you deploying your app.
  2. Then add a google account on it if no account exist on that virtual device.
  3. Right click on the Droid project and make it as Set as Start up Project
  4. Build the project and click Run to delpoy it.

Steps to configure and run the Xamarin.Forms iOS project 

Steps to configure the notification hub for APNS

  1. On your Mac, open Keychain Access. under Category, open My Certificates. Find the certificate you downloaded in the previous section, and disclose its contents. Select only the certificate and export it.
  2. Open Azure portal, select Browse All > App Services, and click your Apps back end. Select Settings, click App Service Push, and then select your notification hub. Move to Apple Push Notification Services > Upload Certificate. Upload the .p12 file, selecting the correct Mode(depending on whether your client SSL certificate from earlier is production or sandbox). Save changes.
Your service is now ready to work with push notifications on Xamarin.Forms iOS.
Now, you will configure the iOS project setting in Xamarin Studio or Visual Studio.
 

Steps to Configure the iOS project in Xamarin Studio

  1. In Xamarin.Studio or visual studio, open Info.plist, and change the bundle identifier with your app bundle identifier.
  2. Move to Background Modes. Check the Enable Background Modes box and the Remote notifications box.
  3. Double-click on your project in the Solution window to view Project Options. 
  4. Select Build, go to iOS Bundle Signing, and select the corresponding identity and the provisional profile.
    You can check the official Xamarin device provisioning documentation, see Xamarin Device Provisioning.

Add push notifications to your iOS app

  1. Open the AppDelegate class, and add the RegisteredForRemoteNotifications override event to register for notifications:
    public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
     {
         Hub = new SBNotificationHub(Constants.ConnectionString, Constants.NotificationHubPath);
    
         Hub.UnregisterAllAsync(deviceToken, (error) =>
         {
             if (error != null)
             {
                 Console.WriteLine("Error calling Unregister: {0}", error.ToString());
                 return;
             }
    
             NSSet tags = null; // create tags if you want
             Hub.RegisterNativeAsync(deviceToken, tags, (errorCallback) =>
             {
                   if (errorCallback != null)
                        Console.WriteLine("RegisterNativeAsync error: " + errorCallback.ToString());
                   });
             });
      }
    2. Also add the ReceivedRemoteNotification event handler override:
     public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
      {
          ProcessNotification(userInfo, false);
      }
    
      void ProcessNotification(NSDictionary options, bool fromFinishedLaunching)
      {
          // Check to see if the dictionary has the aps key.  This is the notification payload you would have sent
          if (null != options && options.ContainsKey(new NSString("aps")))
          {
              //Get the aps dictionary
              NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
    
              string alert = string.Empty;
    
              //Extract the alert text
              // NOTE: If you're using the simple alert by just specifying
              // "  aps:{alert:"alert msg here"}  ", this will work fine.
              // But if you're using a complex alert with Localization keys, etc.,
              // your "alert" object from the aps dictionary will be another NSDictionary.
              // Basically the JSON gets dumped right into a NSDictionary,
              // so keep that in mind.
              if (aps.ContainsKey(new NSString("alert")))
                  alert = (aps[new NSString("alert")] as NSString).ToString();
    
              //If this came from the ReceivedRemoteNotification while the app was running,
              // we of course need to manually process things like the sound, badge, and alert.
              if (!fromFinishedLaunching)
              {
                  //Manually show an alert
                  if (!string.IsNullOrEmpty(alert))
                  {
                      UIAlertView avAlert = new UIAlertView("Notification", alert, null, "OK", null);
                      avAlert.Show();
                  }
               }
       }
    3.Add the following code to the FinishedLaunching method in AppDelegate class:
    // Register for push notifications.
     var settings = UIUserNotificationSettings.GetSettingsForTypes(
         UIUserNotificationType.Alert
         | UIUserNotificationType.Badge
         | UIUserNotificationType.Sound,
         new NSSet());
    
     UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
     UIApplication.SharedApplication.RegisterForRemoteNotifications();
Your app is now configure to support push notifications.
Steps to test push notifications in your Xamarin.Forms iOS app
  1. Make your iOS project as Start up project.
  2. Now build the application and run it on the iOS device.
       Note: You have to allow the push notification permission when the app first launches.