Real-Time Android and iOS Apps using SignalR Core (ASP.NET Core 2.0)

Last year I tried and make an article on Windows 10 UWP app for Desktop and Windows Mobile (a.k.a. Windows Phone) to work with ASP.NET Core SignalR (the early version). As currently the ASP.NET Core team released the SignalR Core 1.0.0-alpha1-final and the .NET team has released the remarkable and awesome .NET Core 2.0, .NET Standard 2.0 and the latest Xamarin 2.4.0 that is compatible with .NET Standard 2.0, I would like to try this in Xamarin mobile apps that is for Android and iOS.

It turn out awesome and I love it. The demo that I created is a very simple UI (User Interface) of Label and Slider controls. Project on the solution:

Add a new class file, in my case I just name it MySignalRHub.cs and add these code:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

usingMicrosoft.AspNetCore.SignalR;

namespaceAspNetCoreSignalR.Hubs

{

/// <summary>

/// My SignalR Hub

/// </summary>

/// <seealso cref="Microsoft.AspNetCore.SignalR.Hub" />

publicclassMySignalRHub:Hub

{

/// <summary>

/// New Update method

/// </summary>

publicvoidNewUpdate(stringcommand,doublestate)

{

Clients.All.InvokeAsync("NewUpdate",command,state);

}

}

}

This is the SignalR hub API and here what you would similar can do.

Before deploying this, we also need to do small configure the ASP.NET Core middleware in the Startup.cs file to use SignalR, CORS and Web Socket and here is what you need to do:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

usingAspNetCoreSignalR.Hubs;

usingMicrosoft.AspNetCore.Builder;

usingMicrosoft.AspNetCore.Hosting;

usingMicrosoft.Extensions.Configuration;

usingMicrosoft.Extensions.DependencyInjection;

namespaceAspNetCoreSignalR

{

/// <summary>

/// Startup Class

/// </summary>

publicclassStartup

{

/// <summary>

/// Initializes a new instance of the <see cref="Startup"/> class.

/// </summary>

publicStartup(IConfiguration configuration)

{

Configuration=configuration;

}

/// <summary>

/// Gets the configuration.

/// </summary>

publicIConfigurationConfiguration{get;}

/// <summary>

/// Configures the services.

/// </summary>

publicvoidConfigureServices(IServiceCollection services)

{

services.AddCors();

services.AddSignalR();

}

/// <summary>

/// Configures the specified application.

/// </summary>

publicvoidConfigure(IApplicationBuilder app,IHostingEnvironment env)

{

app.UseCors(cors=>

{

cors.AllowAnyHeader();

cors.AllowAnyOrigin();

cors.AllowAnyMethod();

});

app.UseWebSockets();

app.UseSignalR(routes=>

{

routes.MapHub<MySignalRHub>("updater");

});

}

}

}

As you can see I also enable Web Sockets which leads us to enable it on Azure as well later.

Compile the project and you can test it by accessing the SignalR endpoint, in my case is:

http://localhost:5000/updater

If you manage to make to this point, we can now deploy this to cloud (Microsoft Azure) by Right-Click the project and choose Publish. You can either use Azure App Services or to your own VM if you already have one. In my case I just create a new Azure App Service (Web App).

As I mentioned earlier, we need to do one thing on Azure which is to enable the Web Sockets. In case of using Azure App Service, just go to your Web App and choose Application Settings. Select ‘On‘ for Web Sockets as shown below.

That’s all for the SignalR hub API and we are now have SignalR Core using ASP.NET Core 2.0 running on Microsoft Azure App Service.

Mobile Client (Xamarin.Forms)

Now, let’s make the mobile app and consume the SignalR hub. First things first is to create a new Cross-Platform project which is Xamarin.Forms using PCL. I name the solution as “MobileClient“.

And choose Blank App with Portable Class Library option for now. Later we convert this PCL to use .NET Standard 2.0.

I was hoping we have this option (just a simple idea). Let’s make a wish we have this option so we don’t have to manually tweak the project.

Also, in this solution, I canceled for the Windows UWP project creation. I only want to target Android and iOS for this demo.

.NET Standard 2.0 option idea

UPDATE: Luckily, I just get the answer from James Montemagno himself regarding this option.

Anyway, let’s update the project to use .NET Standard 2.0 for now. Add a new .NET Standard project on the solution, just name it anything you want. In my case, I just name it first as “MobileClientStandard“.

Secondly, move all the files from “MobileCient” PCL project to the “MobileClientStandard” project. That includes all the XAML files. Here are the steps we need to do:

Delete the class1.cs file on the “MobileClientStandard“.

Move all files (including all the XAML files) from “MobileCient” PCL project to “MobileClientStandard” project.

Then, remove (delete) the “MobileClient” PCL project from the solution.

Then, rename the “MobileClientStandard” back to “MobileClient“. This to make the Assembly name the same as the deleted PCL project.

Lastly, we need to replace the Xamarin.Forms library to the latest version that support .NET Standard. Open the NuGet packages for Solutions level,

And find Xamarin.Forms package and choose the latest Stable version. In my case is version 2.4.0.280 when this blog was created. (latest update:stable version 2.4.0.282).

Once everything done, we can add some codes now. First, add the SignalR client services and the View Model. Here is the folder structure that I made on the .NET Standard 2.0 class library project.

And here is the codes for SignalRClient.cs, ValueChangedEventArgs.cs and MainPageViewModel.cs.

SignalRClient.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

usingMicrosoft.AspNetCore.SignalR.Client;

usingSystem;

usingSystem.Diagnostics;

usingSystem.Threading.Tasks;

namespaceMobileClient

{

/// <summary>

/// SignalR Client

/// </summary>

publicclassSignalRClient

{

privateHubConnection _hub;

publiceventEventHandler<ValueChangedEventArgs>ValueChanged;

publicHubConnectionHub{get{return_hub;}}

/// <summary>

/// Initializes a new instance of the <see cref="SignalRClient"/> class.

/// </summary>

publicSignalRClient()

{

Debug.WriteLine("SignalR Initialized...");

InitializeSignalR().ContinueWith(task=>

{

Debug.WriteLine("SignalR Started...");

});

}

/// <summary>

/// Initializes SignalR.

/// </summary>

publicasyncTask InitializeSignalR()

{

_hub=newHubConnectionBuilder()

.WithUrl("http://[your signalr hub url]/updater")

.Build();

_hub.On<string,double>("NewUpdate",

(command,state)=>ValueChanged?

.Invoke(this,newValueChangedEventArgs(command,state)));

await_hub.StartAsync();

}

/// <summary>

/// Sends the message.

/// </summary>

/// <param name="command">The command.</param>

/// <param name="state">The state.</param>

publicasyncTask SendMessage(stringcommand,doublestate)

{

await_hub?.InvokeAsync("NewUpdate",newobject[]{command,state});

}

}

}

ValueChangedEventArgs.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

usingSystem;

namespaceMobileClient

{

/// <summary>

/// Value Changed EventArgs

/// </summary>

publicclassValueChangedEventArgs:EventArgs

{

/// <summary>

/// Gets the command.

/// </summary>

publicstringCommand{get;privateset;}

/// <summary>

/// Gets the state.

/// </summary>

publicdoubleState{get;privateset;}

/// <summary>

/// Initializes a new instance of the <see cref="ValueChangedEventArgs"/> class.

/// </summary>

/// <param name="command">The command.</param>

/// <param name="state">The state.</param>

publicValueChangedEventArgs(stringcommand,doublestate)

{

Command=command;

State=state;

}

}

}

MainPageViewModel.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

usingSystem.ComponentModel;

usingSystem.Threading.Tasks;

namespaceMobileClient.ViewModels

{

publicclassMainPageViewModel:INotifyPropertyChanged

{

privateintslideValue;

privatereadonlySignalRClient signalR;

#region INotifyPropertyChanged Members

publiceventPropertyChangedEventHandler PropertyChanged;

publicvoidOnPropertyChanged(stringpropertyName)

{

PropertyChanged?

.Invoke(this,newPropertyChangedEventArgs(propertyName));

}

#endregion

/// <summary>

/// Initializes a new instance of the <see cref="MainPageViewModel"/> class.

/// </summary>

publicMainPageViewModel()

{

slideValue=0;

signalR=newSignalRClient();

signalR.ValueChanged+=SignalR_ValueChanged;

}

/// <summary>

/// Handles the ValueChanged event of the SignalR control.

/// </summary>

privatevoidSignalR_ValueChanged(objectsender,ValueChangedEventArgse)

{

slideValue=(int)e.State;

OnPropertyChanged(nameof(SlideValue));

}

/// <summary>

/// Gets or sets the slide value.

/// </summary>

publicintSlideValue

{

get{returnslideValue;}

set

{

if(slideValue!=value)

{

slideValue=value;

Task.Run(async()=>

awaitsignalR.SendMessage("SLIDER",slideValue));

}

}

}

}

}

You just need to add one line of code on the MainPage.xaml.cs code behind file. this is like this:

MainPage.xaml.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

usingMobileClient.ViewModels;

usingXamarin.Forms;

namespaceMobileClient

{

publicpartial classMainPage:ContentPage

{

publicMainPage()

{

InitializeComponent();

// Bind the View Model

BindingContext=newMainPageViewModel();

}

}

}

The last part is the Xamarin.Forms XAML file. We only need to bind the SlideValue. Here is the code: