Xamarin ♥ Azure SignalR Service

Azure SignalR Service is a service in Microsoft Azure that makes it possible for developers to build applications with real-time communications without having to think about how to host it. Azure will handle all that for us.

If you are interested to learn more about SignarlR service and Xamarin apps there is a chapter in the Xamarin.Forms project, the book that I have written together with Johan Karlsson that cover the subject. In the book, you will get step-by-step guidance on how to set up the backend and how to write the app from "file new" to a full app. Read more about the book here, https://www.packtpub.com/application-development/xamarinforms-projects.

This post will cover:

  • How to set up a simple backend built with Azure functions and Azure SignalR service
  • How to use SignalR in a Xamarin App.

Setting up a backend

For the backend, we need to create two services in the Azure portal.

  1. Create a SignalRServcie in a new resource group
  2. Create a Function App in the same resouce group.

Open Visual Studio and create a new Azure Functions projects.

Install the NuGet package, Microsoft.Azure.WebJobs.Extensions.SignalRService. We need to check the "Show prereleases" checkbox to find the package, because it is still just a pre-release.

Create a new function with the name GetSignalRConnectionInfo.

public class GetSignalRConnectionInfo
{
        [FunctionName("GetSignalRConnectionInfo")]
        public static SignalRConnectionInfo Run(
            [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
            [SignalRConnectionInfo(HubName = "chat")] SignalRConnectionInfo connectionInfo)
        {
            return connectionInfo;
        }
}

The SignalRConnectionInfo attribute is used to read the information from the configuration and to generate an access token. If the function should be able to read the information from the configuration we need to add it. What we should add is the connection string that we will find in the Azure Portal in the Keys tab under the SignalR Service. It will be added with the key AzureSignalRConnectionString to the ApplicationSettings of the Function App or to the local.settings.json file in our Visual Studio project if we want to run the function locally. It is also with the attribute we can set the name of the hub that we want to use, we can have multiple hubs in a SignalR Service.

Next step is to create a function that will add messages to the SignalR Service. Create a new function with the name SendMessages.

public class SendMessages
{
        [FunctionName("SendMessages")]
        public async static Task Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
            [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            await signalRMessages.AddAsync(
                new SignalRMessage
                {
                    Target = "chatMessage",
                Arguments = new[] { message }
                });
        }
}

Use the SignalR attribute and an IAsyncCollector parameter to get a collection that you can use for adding messages to the SignalR Service. Target is a key that we can listen after in the app.

If we want to add some logic, for example, some type of validation we can do it here, before we add the message to the collection. In the app we are building in the book can the users upload photos. Then we use Azure Cognitive Services to make sure that the photos not are classified as adult photos.

Authentication

If we want to add authentication to our SignalR Service we should add it to the function that returns the connection info.

Building the app

To build an app with Xamarin we need to install the Microsoft.AspNetCore.SignalR.Client NuGet package.

Create a connection and start listening for messages

First we will create a model for connection information:

public class ConnectionInfo
{
     public string Url { get; set; }
     public string AccessToken { get; set; }
}

Now we can make an HTTP call to the function we created for getting connection information. When we get an answer we will serialize it to a ConnectionInfo model.

var result = await httpClient.GetStringAsync("https://{name-of-your-function}.azurewebsites.net/api/GetSignalRConnectionInfo");
var info = JsonConvert.DeserializeObject<Models.ConnectionInfo>(result);

Now we have all the information that we need to connect to the SignalR Service. To create a connection we will use the HubConnectionBuilder.

var connectionBuilder = new HubConnectionBuilder();
connectionBuilder.WithUrl(info.Url, (Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionOptions obj) =>
{
     obj.AccessTokenProvider = () => Task.Run(() => info.AccessToken);
});
 
var hub = connectionBuilder.Build();

To subscribe for messages we will use the On<object> method on the hub. We will pass the key that we specified in the message and an Action, in this case, we using an expression for the action.

In the expression body, we will handle the message, the message will come as a string so we need to convert the object to a string and then we can deserialize it to the type of message that we will receive. In this case, I have created a model with the name Message.

The last thing to do to start to listen for messages is to call the StartAsync method on the hub.

hub.On<object>("chatMessage", (message) =>
{
      var json = message.ToString();
      var obj = JsonConvert.DeserializeObject<Message>(json);
 
      //Handle the message here
});
 
await hub.StartAsync();

Sending a message

To sending a message is just a simple POST to the SendMessages function.

public async Task SendMessage(Message message)
{
     var json = JsonConvert.SerializeObject(message);
 
     var content = new StringContent(json, Encoding.UTF8, "application/json");
 
     var response = await httpClient.PostAsync("https://xfbook.azurewebsites.net/api/SendMessages", content);
}

OnSuspend and OnResume

It is important to handle the suspend and resumes events. The best way to that is to override OnSuspend and OnResume in App.xaml.cs. I recommended that you create a Dispose method in the same class as we have the other code that is related to SignalR. In this method, we will stop listening for messages and dispose the hub.

public async Task Dispose()
{
     if(hub != null)
     {
          await hub.StopAsync();
          await hub.DisposeAsync();
     }
}

And then we can call the Dispose method from the OnSuspend method. In the OnResume method, we can start the connection again.

Xamarin Month

This blog post is a part of Xamarin Month. Follow this link to read all the other blog post of the Xamarin Month.

Real time communication with signalR

If you need realtime communication and don't want to use long polling ajax calls for example for an game or a chat and also want fallback if WebSockets not is supported, signalR is a good library. This is a short tutorial on how to use signalR in an ASP.NET MVC application.

If you want to use a WebSocket connection if it is supported by the client you need to use IIS8 and enable the WebSocket Protocol, read this post for a step by step guide.

If websocket is not supported, signalR has fallback to server side events and ajax long polling, so web browsers without support for websockets can also use signalR.

Add signalR libraries

The easiest way to add signalR to your web application is to use NuGet. It will add the assemblies and javascript libraries needed to the web project.

Install-Package Microsoft.AspNet.SignalR

Server side

First you need to set "aspnet:UseTaskFriendlySynchronizationContext" to true in settings in your Web.config if you want to use websocket.

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
  </appSettings>

Next step is to create a new class that inherits from PersistentConnection

public class MyConnection : PersistentConnection
{
}

In this new class you can override the methods OnConnected, OnReceived and OnDisconnected.

OnConnected
This method is called when a client connecting. Here can you for an example add users to users online lists.

OnReceived
This method is called when a message from the client is received, you send it further to your buisness logic or an other user (or users).

OnDisconnected
This method is called when a client disconnecting. Here can you for an example remove users from users online lists.

In my example I have a player object that handles logic for a player. To keep the player object alive I add a static dictionary with the connection id as key.

public class MyConnection : PersistentConnection
{
        private static Dictionary<string, Player> _players;
 
        protected override System.Threading.Tasks.Task OnConnected(IRequest request, string connectionId)
        {
            if (_players == null)
            {
                _players = new Dictionary<string, Player>;();
            }
 
            var player = new Player();
 
            _players.Add(connectionId, player);
 
            player.SomethingHappend += (sender, args) =&gt; Connection.Send(connectionId, "Hello!");
 
            return base.OnConnected(request, connectionId);
        }
 
        protected override System.Threading.Tasks.Task OnReceived(IRequest request, string connectionId, string data)
        {
            var player = _players[connectionId];
            player.DoStuff(data);
 
            return base.OnReceived(request, connectionId, data);
        }
 
        protected override System.Threading.Tasks.Task OnDisconnected(IRequest request, string connectionId)
        {
            var player = _players[connectionId];
 
            player.Dispose();
 
            return base.OnDisconnected(request, connectionId);
}
        }

Before you can start using your signalR server you need to map a route in Global.asax.cs. Add the route to the Application_Start method before other route mappings.

protected void Application_Start()
{
            RouteTable.Routes.MapConnection<MyConnection>("connection", "/my");
}

The url to the signalR server will now be http://pageurl/connection/my.

Connection.Send(connectionId,message) sending a message to a specific user using the connection id. It make's it possible to have communications between two clients.

If you want to send a message to all connected users you should use Connection.Broadcast(message).

Client side

Start to add a reference to the signalR javascript library.

Connect
To connect to the signalR server use the relative url that you created with the mapping in Global.asax.cs.
If you want to know when the connection has successfully established use callback on the start function.
Use the stateChanged event if you want to know when states changes on the connection. For example you can use it for to tell users that connection has been lost.

The connection has four states:

  • Connecting
  • Connected
  • Reconnecting
  • Disconnected
var myConnection = $.connection('/connection/my');
myConnection.start(callback);
        myConnection.stateChanged(function (change) {
            if (change.newState === $.signalR.connectionState.connected) {
 
            }
        });

Send message
To send a message use the send(message) method on the connection object.

myConnection.Send(message);

Received

myConnection.received(function (message) {
     //Handle the message here.
});

That is all. It is not harder than that to use real time communication in your web application.