{"id":2105,"date":"2015-11-16T00:00:00","date_gmt":"2015-11-03T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/using-signalr-to-create-mobile-applications\/"},"modified":"2021-06-03T16:47:10","modified_gmt":"2021-06-03T16:47:10","slug":"using-signalr-to-create-mobile-applications","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/using-signalr-to-create-mobile-applications\/","title":{"rendered":"Using SignalR to Create Mobile Applications"},"content":{"rendered":"<div id=\"pretty\">\n<p class=\"start\"> SignalR is a  powerful, open source library that allows developers to quickly and easily  implement real-time communications across different platforms. Although SignalR  is predominantly used in web applications, the library is versatile enough to be  used in mobile apps as well. This means that you can implement native real-time  connected applications without requiring the use of a web view. SignalR provides  client libraries for .NET, JavaScript, Windows Phone, Windows Universal  applications, iOS and Android.:p&gt;<\/p>\n<p> There are two  ways to add SignalR to cross-platform mobile applications. For .NET developers,  there is the option to &#8220;write-once deploy-everywhere, leveraging the power of C#  and Xamarin. Xamarin is a 3rd party library that takes advantage of the .NET  framework and provides a unified SDK that binds directly to the native libraries  in order develop mobile applications. Because Xamarin works on top of the .NET  framework, it means that all that&#8217;s needed in order to use SignalR is to add the  necessary .dll to the solution, usually through NuGet.<\/p>\n<p> If you don&#8217;t  want to go down the route of Xamarin, there are also native ports of the SignalR  library both in Objective-C and Java. For iOS development you can use  <a href=\"https:\/\/github.com\/DyKnow\/SignalR-ObjC\"> this  port&#160;(SignalR-ObjC)<\/a><span class=\"MsoHyperlink\">, <\/span> and for Android development there&#8217;s  <a href=\"https:\/\/github.com\/SignalR\/java-client\"> this port  (SignalR\/java-client)<\/a>. There is,  however, a small caveat when using these ports: because these two libraries are  not officially supported by the SignalR community, there is a chance that they  may be out of date or have features missing.<\/p>\n<p> In this article,  we are going to develop a sample Windows Universal Application (store and  mobile) and implement basic real-time communications between the application and  a back-end service using SignalR. <\/p>\n<p><span class=\"Heading2Char\"> The  Server setup<\/span><\/p>\n<p>  To&#160;demonstrate the real-time communication framework, we are going to implement  a sample stock price service. &#160;Clients&#160;that are connected through SignalR will  receive real-time stock prices when these are updated on&#160;the server. The server  will use just a mock service to generate dummy prices for the purpose of this  demo. In a production environment, this data would be coming from a real API.<\/p>\n<p> The SignalR  hub will be installed on a WebAPI 2.0 running on top of ASP.NET 4.5. The hub  will only have one public method which will be used to broadcast the data to all  connected clients.&#160;<\/p>\n<p> On a new,  empty&#160;WebAPI solution in Visual Studio, open the NuGet console manager and run  the following command:<\/p>\n<pre>Install-Package Microsoft.AspNet.SignalR<\/pre>\n<p> This will  install SignalR&#160;along with&#160;the necessary dependencies such as Owin (ASP.NET&#8217;s  middleware framework). Owin is used to bootstrap SignalR and route all incoming  SignalR requests to the hub.<\/p>\n<p> By default,  SignalR hubs are configured to reject incoming request from external  domains.&#160;This means that if your SignalR hub (WebAPI) is running under the  https:\/\/contoso.com and clients are trying to connect from https:\/\/mydomain.com,  then the requests will fail with an HTTP 500 error. &#160;To fix this problem, we  can&#160;configure Owin&#160;to accept&#160;cross-domain requests (CORS). &#160;Go back to the NuGet  console manager and run the following command:<\/p>\n<pre>Install-Package Microsoft.Owin.Cors<\/pre>\n<p> With the  dependencies installed, we can now start configuring the hub. Right-click on the  WebAPI project and add a new class. Name the class <b>Startup.cs<\/b> and paste the following code:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using Microsoft.AspNet.SignalR;\nusing Microsoft.Owin.Cors;\nusing Owin;\n\nnamespace SignalRService\n{\n&#160;&#160;&#160; public class Startup\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public void Configuration(IAppBuilder app)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; app.Map(\"\/signalr\", map =&gt;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; map.UseCors(CorsOptions.AllowAll);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var hubConfiguration = new HubConfiguration { };\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; map.RunSignalR(hubConfiguration);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; });\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n<\/pre>\n<p> This code  bootstraps SignalR and configures CORS in order to allow cross-domain clients to  connect. At the moment all external clients can hit the hub because we defined <b>CorsOption.AllowAll<\/b>. Unless you wish  to create a truly open API, it is advisable that you restrict access to the  service.<\/p>\n<p> Before we  create the hub, we need a mock service to feed stock data to the clients. Add a  new class to the project and name it <b> StockService.&#160;<\/b>Paste the following code in it:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using Microsoft.AspNet.SignalR;\nusing Newtonsoft.Json;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Threading;\n\nnamespace SignalRService\n{\n&#160;&#160;&#160; public class StockService\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private static Timer timer;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private static Random random;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public ConcurrentDictionary&lt;string, decimal&gt; Stocks;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private static int interval = 5000;\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public StockService()\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; random = new Random();\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; timer = new Timer(UpdateStockPrices, null, interval, interval);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Stocks = new ConcurrentDictionary&lt;string, decimal&gt;();\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Stocks.TryAdd(\"GOOG\", new decimal(2.50));\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Stocks.TryAdd(\"MSFT\", new decimal(3.15));\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Stocks.TryAdd(\"APPL\", new decimal(4.57));\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private void UpdateStockPrices(object state)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (var stock in Stocks.Keys)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Stocks[stock] = random.NextDecimal();\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; BroadcastStockPriceChanges();\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public void BroadcastStockPriceChanges()\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var hub = GlobalHost.ConnectionManager.GetHubContext&lt;StocksHub&gt;();\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; hub.Clients.All.sendStockData(JsonConvert.SerializeObject(Stocks));\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n<\/pre>\n<p> The service  is pretty basic. It creates a timer which is used to push stock data at a 5  second interval. There are a couple of extension methods that are used to  generate random decimals. Add a new class to your project and name it <b>Extensions<\/b>. Paste the following code:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using System;\n\nnamespace SignalRService\n{\n&#160;&#160;&#160; public static class Extensions\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public static int NextInt32(this Random rng)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; unchecked\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; int firstBits = rng.Next(0, 1 &lt;&lt; 4) &lt;&lt; 28;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; int lastBits = rng.Next(0, 1 &lt;&lt; 28);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return firstBits | lastBits;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public static decimal NextDecimal(this Random rng)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; byte scale = (byte)rng.Next(29);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; bool sign = rng.Next(2) == 1;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new decimal(rng.NextInt32(),\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rng.NextInt32(),\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rng.NextInt32(),\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; sign,\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; scale);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n<\/pre>\n<p> With the  service in place, the last step in creating the service process is adding the  SignalR hub to relay data to and from the clients. Right-click on the WebAPI  project and add a new class. Name it <b> StocksHub<\/b> and paste the following code:&#160;<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using Microsoft.AspNet.SignalR;\n\nnamespace SignalRService\n{\n&#160;&#160;&#160; public class StocksHub : Hub\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private readonly StockService stockService;\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public StocksHub()\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; stockService = new StockService();\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n<\/pre>\n<p> I understand  your surprise. The hub is, in fact, empty. We only use it to instantiate the <b>StockService<\/b>. This is the beauty of  SignalR. The hub can act as the conduit -passing information back and forth  between the client and some arbitrary back-end service. This is all we need to  do to get a functional hub. Next, we&#8217;ll look into what&#8217;s required to allow a  Windows Universal application to receive data from the SignalR hub.<\/p>\n<h2>The Client Setup<\/h2>\n<p>The steps  used to enable SignalR on the Windows Universal apps are the exact same steps  you would follow if you were working on a Xamarin application. Open up Visual  Studio and create a new Windows Universal app. In the end, the solution should  consist of 3 projects:<\/p>\n<ul>\n<li>&#160;Windows Phone Application<\/li>\n<li>Windows Desktop Application<\/li>\n<li>Shared project<\/li>\n<\/ul>\n<p> We will use  the shared project to add the common logic, such as the SignalR client and  configuration settings. The individual projects will have only the UI specific  logic necessary to display the data retrieved through SignalR.<\/p>\n<p> We&#8217;ll start  by adding the SignalR packages. Open the NuGet console manager and run the  following command:<\/p>\n<pre>Install-Package Microsoft.AspNet.SignalR.Client<\/pre>\n<p> Next, open up <b>App.xaml.cs<\/b> and add the following  public property and private method in the class:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">public SignalRClient Client { get; set; }\nprivate async Task StartHub()\n{\n&#160;&#160;&#160; Client = new SignalRClient();\n&#160;&#160;&#160; await Client.Connect();\n}\n<\/pre>\n<p> Change the <b>OnLaunched()<\/b> method to be <b>async<\/b> by changing the method  declaration like this:<\/p>\n<pre>protected override async void OnLaunched(LaunchActivatedEventArgs e)&#160;<\/pre>\n<p> and add the  following line anywhere in the body of the method:<\/p>\n<pre>await StartHub();<\/pre>\n<p> The above  code will start up the client and connect to the remote hub in order to start  receiving data. Next, since we&#8217;re working with XAML, we can take advantage of  data binding so that we can update the UI asynchronously and automatically when  new data is received. In the shared project, create a new class and name it <b>PageData.&#160;<\/b>Add the following code:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using System.ComponentModel;\n\nnamespace SignalRApp\n{\n&#160;&#160;&#160; public class PageData : INotifyPropertyChanged\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private double _GOOG;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private double _MSFT;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private double _APPL;\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public event PropertyChangedEventHandler PropertyChanged;\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public double GOOG\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; get { return _GOOG; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; set\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _GOOG = value;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; NotifyPropertyChanged(\"GOOG\");\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public double MSFT\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; get { return _MSFT; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; set\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _MSFT = value;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; NotifyPropertyChanged(\"MSFT\");\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public double APPL\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; get { return _APPL; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; set\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _APPL = value;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; NotifyPropertyChanged(\"APPL\");\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public void NotifyPropertyChanged(string propertyName)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (PropertyChanged != null)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PropertyChanged(this,\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; new PropertyChangedEventArgs(propertyName));\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n<\/pre>\n<p> In a proper  application, we would opt in for proper MVVM framework that can eliminate most  of the above boiler plate code. Nonetheless,&#160;this is a demo app with limited  requirements so there&#8217;s no reason to complicate the design and add unnecessary  dependencies. Finally, we need to create the SignalR client. Add a new class to  the shared project&#160;and name it <b> SignalRClient.&#160;<\/b>Paste the code below:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using Microsoft.AspNet.SignalR.Client;\nusing Microsoft.AspNet.SignalR.Client.Transports;\nusing System;\nusing System.Threading.Tasks;\n\nnamespace SignalRApp\n{\n&#160;&#160;&#160; public class SignalRClient\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private readonly HubConnection connection;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private readonly IHubProxy proxy;\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public event EventHandler&lt;dynamic&gt; OnDataReceived;\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public SignalRClient()\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; connection = new HubConnection(\"http:\/\/localhost:9246\");\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy = connection.CreateHubProxy(\"StocksHub\");\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public async Task Connect()\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; await connection.Start(new WebSocketTransport());\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy.On(\"sendStockData\", (dynamic data) =&gt; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (OnDataReceived != null)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; OnDataReceived(this, data);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; });\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n<\/pre>\n<p> In the code  above, we create a proxy and then we define an event handler that listens for a  &#8220;<b>sendStockData()<\/b>&#8221; method. Notice how  this event name matches exactly the<b> StockService <\/b>method we defined in our hub on the server. This  instructs the client to raise an event every time the server calls that method.&#160;<\/p>\n<p> At the point  of writing this, &#8216;Windows Universal apps&#8217; seems to also suffer from a small bug  when connecting to a hub. The connection process is particularly slow due to  protocol failures. To resolve this problem, we need to define the transport  protocol during our attempt to instantiate the connection. This is achieved here  by requesting that the connection uses the Web Sockets protocol:<\/p>\n<pre>await connection.Start(new WebSocketTransport());<\/pre>\n<p> Finally, it&#8217;s  worth noting that when creating a new <b> HubConnection()<\/b> we need to pass the hub URL, which is the same as the WebAPI  URL. Make sure you use the right URL otherwise your connection will fail.<\/p>\n<p> The last step  is to update the UI in the phone and desktop apps to use the SignalR data. On  the Windows phone project, open the <b> MainWindow.xaml<\/b> and replace the <b> &lt;Grid&gt;&lt;\/Grid&gt;<\/b> root element with the code below:<\/p>\n<div class=\"smalllisting\">\n<p>  &lt;Grid.RowDefinitions&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;RowDefinition  Height=&#8221;150&#8243;&gt;&lt;\/RowDefinition&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;RowDefinition  Height=&#8221;*&#8221;&gt;&lt;\/RowDefinition&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;\/Grid.RowDefinitions&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;TextBlock  Grid.Row=&#8221;0&#8243;  Style=&#8221;{StaticResource  HeaderTextBlockStyle}&#8221;  Margin=&#8221;25,0,0,0&#8243;&gt;STOCK  PRICES&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;StackPanel  Grid.Row=&#8221;1&#8243;  Margin=&#8221;25,0,0,0&#8243;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;StackPanel  Orientation=&#8221;Horizontal&#8221;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  BodyTextBlockStyle}&#8221;  Width=&#8221;70&#8243;&gt;GOOG:&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  BodyTextBlockStyle}&#8221;  Text=&#8221;{Binding  GOOG  }&#8221;&gt;&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;\/StackPanel&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;StackPanel  Orientation=&#8221;Horizontal&#8221;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  BodyTextBlockStyle}&#8221;  Width=&#8221;70&#8243;&gt;MSFT:&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock   &#160;Style=&#8221;{StaticResource  BodyTextBlockStyle}&#8221;  Text=&#8221;{Binding  MSFT}&#8221;&gt;&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;\/StackPanel&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;StackPanel  Orientation=&#8221;Horizontal&#8221;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  BodyTextBlockStyle}&#8221;  Width=&#8221;70&#8243;&gt;APPL:&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  BodyTextBlockStyle}&#8221;  Text=&#8221;{Binding  APPL}&#8221;&gt;&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;\/StackPanel&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;\/StackPanel&gt;<\/p>\n<\/p><\/div>\n<p> We also need  to add the necessary code to retrieve and bind the data to the UI View, so open <b>MainView.xaml.cs<\/b> and paste the code  below:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using Newtonsoft.Json;\nusing Windows.UI.Core;\nusing Windows.UI.Xaml.Controls;\nusing Windows.UI.Xaml.Navigation;\n\nnamespace SignalRApp\n{\n&#160;&#160;&#160; public sealed partial class MainPage : Page\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private PageData pageData;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public MainPage()\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.InitializeComponent();\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.NavigationCacheMode = NavigationCacheMode.Required;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData = new PageData();\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.DataContext = pageData;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var app = App.Current as App;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; app.Client.OnDataReceived += Client_OnDataReceived;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private void Client_OnDataReceived(object sender, dynamic e)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Dispatcher.RunAsync(CoreDispatcherPriority.Normal,\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; () =&gt;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var result = JsonConvert.DeserializeObject&lt;PageData&gt;(e);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData.APPL = result.APPL;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData.GOOG = result.GOOG;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData.MSFT = result.MSFT;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; });\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n<\/pre>\n<p> Since this is  a universal application, we need to apply similar code changes to the Windows  Store project. Go to the <b>MainView.xaml<\/b>  and paste the following code, which should replace the root <b>&lt;Grid&gt;&lt;\/Grid&gt;<\/b> element:<\/p>\n<div class=\"smalllisting\">\n<p>  &lt;Grid  Background=&#8221;{ThemeResource  ApplicationPageBackgroundThemeBrush}&#8221;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;Grid.RowDefinitions&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;RowDefinition  Height=&#8221;150&#8243;&gt;&lt;\/RowDefinition&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;RowDefinition  Height=&#8221;*&#8221;&gt;&lt;\/RowDefinition&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;\/Grid.RowDefinitions&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;TextBlock  Grid.Row=&#8221;0&#8243;  Style=&#8221;{StaticResource  HeaderTextBlockStyle}&#8221;  Margin=&#8221;25,20,0,0&#8243;&gt;STOCK  PRICES&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;StackPanel  Grid.Row=&#8221;1&#8243;  Margin=&#8221;25,0,0,0&#8243;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;StackPanel  Orientation=&#8221;Horizontal&#8221;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  SubheaderTextBlockStyle}&#8221;  Width=&#8221;120&#8243;&gt;GOOG:&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  SubheaderTextBlockStyle}&#8221;  Text=&#8221;{Binding  GOOG  }&#8221;&gt;&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;\/StackPanel&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;StackPanel  Orientation=&#8221;Horizontal&#8221;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  SubheaderTextBlockStyle}&#8221;  Width=&#8221;120&#8243;&gt;MSFT:&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock   &#160;Style=&#8221;{StaticResource  SubheaderTextBlockStyle}&#8221;  Text=&#8221;{Binding  MSFT}&#8221;&gt;&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;\/StackPanel&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;StackPanel  Orientation=&#8221;Horizontal&#8221;&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  SubheaderTextBlockStyle}&#8221;  Width=&#8221;120&#8243;&gt;APPL:&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;TextBlock  Style=&#8221;{StaticResource  SubheaderTextBlockStyle}&#8221;  Text=&#8221;{Binding  APPL}&#8221;&gt;&lt;\/TextBlock&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;   &lt;\/StackPanel&gt;<\/p>\n<p>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;    &lt;\/StackPanel&gt;<\/p>\n<p>  &#160;&#160;&#160;   &lt;\/Grid&gt;<\/p>\n<\/p>\n<\/div>\n<p> And the  equivalent code-behind to provide the data for the view:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">using Newtonsoft.Json;\nusing Windows.UI.Core;\nusing Windows.UI.Xaml.Controls;\n\nnamespace SignalRApp\n{\n&#160;&#160;&#160; public sealed partial class MainPage : Page\n&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private PageData pageData;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  public MainPage()\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.InitializeComponent();\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData = new PageData();\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.DataContext = pageData;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var app = App.Current as App;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; app.Client.OnDataReceived += Client_OnDataReceived;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  private void Client_OnDataReceived(object sender, dynamic e)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Dispatcher.RunAsync(CoreDispatcherPriority.Normal,\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; () =&gt;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var result = JsonConvert.DeserializeObject&lt;PageData&gt;(e);\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData.APPL = result.APPL;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData.GOOG = result.GOOG;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; pageData.MSFT = result.MSFT;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; });\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\n&#160;&#160;&#160; }\n}\n\n\n<\/pre>\n<p> To test the  whole system, run the WebAPI first and then run the Windows Phone and Store apps  in the order you like. The end result should look like this:<\/p>\n<p> <img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2301-imgE4.jpg\" alt=\"2301-imgE4.jpg\" \/><\/p>\n<p>  <img loading=\"lazy\" decoding=\"async\" height=\"167\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2301-clip_image004.jpg\" width=\"623\" alt=\"2301-clip_image004.jpg\" \/><\/p>\n<h2> Conclusion<\/h2>\n<p> In this article, we saw how easy it is to get started with real-time communication across  platforms using SignalR. It doesn&#8217;t matter if you&#8217;re targeting web, mobile or  desktop platforms. In the end, we&#8217;ve&#160;proved that SignalR requires minimal code  to provide something that in the past was clunky, unreliable and very hard to  implement. I would like to close with Arthur C. Clarke&#8217;s quote&#160;- &#8220;<i>Any  sufficiently advanced technology is indistinguishable from magic<\/i>&#8220;. SignalR  is advanced enough to make real-time communications seem like magic.<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>SignalR isn&#8217;t just for web applications. It can also provide the basic real-time communications for a connected Windows Universal Application or even iOS and Android applications. Christos Matskas demonstrates how to get started with creating applications across a range of platforms that require real-time communication using SignalR&hellip;<\/p>\n","protected":false},"author":38246,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4178,4359,4896],"coauthors":[],"class_list":["post-2105","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-bi","tag-development","tag-mobile"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2105","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/38246"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=2105"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2105\/revisions"}],"predecessor-version":[{"id":91250,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2105\/revisions\/91250"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=2105"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=2105"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=2105"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=2105"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}