Real Time Communication To Cross Platform Devices Using Azure SignalR Service - Part Two

Real Time communication between cross platform devices such as Azure hosted .net core web app, xamarin based android app and .Net Framework based windows form app using Azure SignalR Service.

Introduction

In my previous article , we learned about how to setup the SignalR Service in Azure Portal. In this article, we will develop the SignalR client applications to connect to Azure SignalR Service to do real time communication between cross platform devices. We are going to develop the following applications. 
  • .Net Core We App to send and receive the data from the browser.
  • .Net Framework (v4.6.1) based windows forms application that running in system tray with the global hot key registered (CTRL + SHIFT + C) to push the clipboard content to other devices whenever the hotkey is pressed.
  • Xamarin based Android App to receive the data from SignalR Hub in Local Notification Window. 
Web App SignalR Client App

This is an Asp.net Core Web Application with layout defined using bootstrap library. In the landing page, we will be adding DIV container to show all the incoming messages from SignalR Hub and in the bottom of the page, we will place the text box and button to publish the text message to other clients. We will use the SignalR Javascript Client library to connect to SignalR Hub. 

Steps 

Launch the Visual Studio and Create a New Project (.Net Core -> Asp.net Core Web Application) . You must have the latest .Net Core SDK (2.1) installed on your machine.

Xamarin

Install the Microsoft.Azure.SignalR Nuget Package for your project.
Xamarin
 
By default, the Microsoft.AspNetCore.SignalR package contains its server libraries as part of its ASP.NET Core Web Application template. However, the JavaScript client library for SignalR must be installed using npm. Use the following commands from Node Package Manager Console to install it and copy the signalr.js file from node_modules\@aspnet\signalr\dist\browser to wwwroot\lib\signalr\signalr.js. (Create a SignalR Folder under Lib Directory) 
  1. npm init -y  
  2. npm install @aspnet/signalr  
We will create a new hub called MagicPasteHub that internally connects to Azure Service. Right click on the Project in Solution Explorer and Create a New Folder called Hub and then add a new file called MagicPasteHub.cs and paste the following code.
  1. publicclass MagicPasteHub: Hub {  
  2.     public async void SendData(string data) {  
  3.         await Clients.All.SendAsync("ReceiveData", data);  
  4.     }  
  5. }  
In the Startup.cs, add the following code in ConfigureServices method. SignalREndPoint Key will hold the Azure Endpoint Value. During Development time, you can store the Azure Endpoint Values in UserSecrets.json. The method AddAzureSignalR will establish the link between Web App and Azure SignalR library using the EndPoint URL.
  1. services.AddSignalR().AddAzureSignalR(Configuration["AppSettings:SignalREndPoint"])  
  2. .AddHubOptions<magicpastehub>(a => a.EnableDetailedErrors = true);  
Add the following code in Configure Method to map the AzureSignalRService with MagicPasteHub Route URL
  1. app.UseAzureSignalR(routes => {  
  2.     routes.MapHub < magicpastehub > ("/MagicPaste");  
  3. });  
In the site.js, add the following code to setup the signalR client to connect to hub and wire up the button click event to send the message and wire up the ReceiveData event to append the incoming data into DIV container.
  1. const connection = new signalR.HubConnectionBuilder().withUrl("/MagicPaste").build();  
  2. connection.start();  
  3. $("#btnPublish").click(function() {  
  4.     var msg = $("#inputData").val();  
  5.     if (msg.length > 0) {  
  6.         connection.invoke("SendData", msg).catch(err => console.error(err.toString()));  
  7.         $("#inputData").val('');  
  8.     }  
  9. });  
  10. connection.on("ReceiveData", (msg) => {  
  11.             $("#messagesList").append($(" < li class = "list-group-item list-group-item-info" > ").html(msg));  
  12.             });  
  13.             $('#inputData').keypress(function(event) {  
  14.                 if (event.keyCode == 13) {  
  15.                     $('#btnPublish').click();  
  16.                 }  
  17.             });  
The User Interface of the web app will look like below. I have hosted this app into Azure with Free Tier Resource Group. You can follow the microsoft official docs for how to publish web app into Azure Portal.

Xamarin 

Windows Forms App SignalR Client

This is a full .net framework based windows forms app that will be running in the system tray with the registered global hotkey CTRL + SHIFT + C. Whenever hot key is pressed anywhere in the desktop, app will check and publish the content of the clipboard to other clients. I used NHotKey open source library for registering the hotkey in Windows Forms.

Xamarin 

Steps

Launch the Visual Studio and Click on File -> New Project and Select Windows Forms Application under Windows Desktop Section.

After creating the application, Install the NuGet Package of Microsoft.AspNetCore.SignalR.Client Library for the signalr support.

Xamarin

In the MainForm.cs add the following code. The code below will register the global hotkey (CTRL + SHIFT + C) and also initialize the connection with SignalR hub.
  1. private void MainForm_Load(object sender, EventArgs e) {  
  2.     HotkeyManager.Current.AddOrReplace("OnHotKeyPressed", Keys.Control | Keys.Shift | Keys.C, true, OnHotKeyPressed);  
  3.     client.Initialize();  
  4.     this.WindowState = FormWindowState.Minimized;  
  5.     MinimizeToTray();  
  6.     this.ShowInTaskbar = false;  
  7. }  
Add the new File called AzureSignalRClient.cs. This is the wrapper class to to put all the Azure SignalR Client related code.
  1. publicclass AzureSignalRClient {  
  2.     private MainForm mainForm;  
  3.     public AzureSignalRClient(MainForm _mainForm) {  
  4.         mainForm = _mainForm;  
  5.     }  
  6.     private HubConnection connection;  
  7.     TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();  
  8.     public async void Initialize() {  
  9.         connection = new HubConnectionBuilder().WithUrl("https://magicpaste.azurewebsites.net/MagicPaste").Build();  
  10.         connection.On < string > ("ReceiveData", (msg) => {  
  11.             mainForm.Invoke((Action)(() => mainForm.txtIncomingData.Text = msg));  
  12.         });  
  13.         try {  
  14.             await connection.StartAsync();  
  15.         } catch (Exception e) {  
  16.             Console.WriteLine(e.Message);  
  17.         }  
  18.     }  
  19.     public async void SendData(string msg) {  
  20.         await connection.InvokeAsync("SendData", msg);  
  21.     }  
  22.     public async void Close() {  
  23.         await connection.StopAsync();  
  24.     }  
  25. }  
When the form gets loaded, it initializes the connection to SignalRHub and whenever the hot key is pressed, it will send the data to Azure Hub.

Xamarin SignalR Client Android App

This is a Xamarin-based Android app which connects to SignalR hub to receives the messages and shows as notifications to the user and then when the user is clicking the notification, it opens the application with the list of content shared so far in the list view. 

Xamarin

Steps

Launch Visual Studio and select the Android -> Android App (Xamarin) from the Menu.
Xamarin 
 
From the Solution Explorer, Select Manage NuGet Packages and Select Microsoft.AspNetCore.SignalR.Client Library and Install it.
Xamarin

In MainActivity.cs, Initialize the SignalR Connection as part of OnCreate Method and wire up the SignalR Receive Data event to send the Notification with the received message. I used SharedPreferences to store all the incoming data as Json and display it in the List Activity.
  1. publicclass MainActivity: ListActivity {  
  2.     protectedoverridevoid OnCreate(Bundle savedInstanceState) {  
  3.         base.OnCreate(savedInstanceState);  
  4.         ListAdapter = new ArrayAdapter < string > (this, Android.Resource.Layout.SimpleListItem1, GetItemsFromPreferences());  
  5.         InitializeSignalRClient();  
  6.     }  
  7.     private async void InitializeSignalRClient() {  
  8.         var connection = new HubConnectionBuilder().WithUrl("https://magicpaste.azurewebsites.net/MagicPaste").Build();  
  9.         connection.On < string > ("ReceiveData", (msg) => {  
  10.             SaveData(msg);  
  11.             ShowNotification(msg);  
  12.         });  
  13.         try {  
  14.             await connection.StartAsync();  
  15.         } catch (Exception e) {  
  16.             Log.Error("MagicPaste", e.Message);  
  17.         }  
  18.     }  
  19.     private List < string > GetItemsFromPreferences() {  
  20.         // get shared preferences  
  21.         ISharedPreferences pref = Application.Context.GetSharedPreferences("MagicPaste", FileCreationMode.Private);  
  22.         // read exisiting value  
  23.         var itemsFromSP = pref.GetString("Items"null);  
  24.         // if preferences return null, initialize listOfCustomers  
  25.         if (itemsFromSP == null) returnnew List < string > ();  
  26.         var items = JsonConvert.DeserializeObject < List < string >> (itemsFromSP);  
  27.         if (items == null) returnnew List < string > ();  
  28.         return items;  
  29.     }  
  30.     privatevoid SaveData(string msg) {  
  31.         // get shared preferences  
  32.         ISharedPreferences pref = Application.Context.GetSharedPreferences("MagicPaste", FileCreationMode.Private);  
  33.         // read exisiting value  
  34.         var itemsFromSP = pref.GetString("Items"null);  
  35.         IList < string > items;  
  36.         // if preferences return null, initialize listOfCustomers  
  37.         if (itemsFromSP == null) items = new List < string > ();  
  38.         else items = JsonConvert.DeserializeObject < List < string >> (itemsFromSP);  
  39.         // add your object to list of customers  
  40.         items.Add(msg);  
  41.         // convert the list to json  
  42.         var itemsAsJson = JsonConvert.SerializeObject(items);  
  43.         ISharedPreferencesEditor editor = pref.Edit();  
  44.         // set the value to Customers key  
  45.         editor.PutString("Items", itemsAsJson);  
  46.         // commit the changes  
  47.         editor.Commit();  
  48.     }  
  49.     privatevoid ShowNotification(string msg) {  
  50.         Intent intent = new Intent(thistypeof(MainActivity));  
  51.         // Create a PendingIntent; we're only using one PendingIntent (ID = 0):  
  52.         constint pendingIntentId = 0;  
  53.         PendingIntent pendingIntent = PendingIntent.GetActivity(this, pendingIntentId, intent, PendingIntentFlags.CancelCurrent);  
  54.         Notification.Builder builder = new Notification.Builder(this).SetContentIntent(pendingIntent).SetContentTitle("MagicPaste").SetContentText(msg).SetAutoCancel(true).SetSmallIcon(Resource.Drawable.notification_tile_bg);  
  55.         // Build the notification:  
  56.         Notification notification = builder.Build();  
  57.         // Get the notification manager:  
  58.         NotificationManager notificationManager = GetSystemService(NotificationService) as NotificationManager;  
  59.         // Publish the notification:  
  60.         constint notificationId = 0;  
  61.         notificationManager.Notify(notificationId, notification);  
  62.     }  
  63. }  
Conclusion

SignalR services are mainly used for Apps with Real-Time technologies with high-frequency data flows and large quantities of concurrent connections between the client and server. Now with Azure SignalR Service, it allows you to use ASP.NET Core SignalR to build real-time experiences such as chat, live dashboards, colloboration editor and more, all without worrying about capacity provisioning, scaling, or persistent connections. This article explains the basic idea of how to use Azure SignalR Service However, for the real world implementation, we need to look at the fact of application performance and battery optimization for mobile apps before implementing SignalR services.

I have uploaded the entire source code of Web, Android and WinForms in my github repository.

Happy Coding.