0 Comments

ShellLoginApp

Since the release of Xamarin Forms 4.5, Shell now supports modal navigation. Since one of my highest ranking blog posts is how to create a login page with Xamarin Forms. I thought it was time to revisit the topic and look at how to implement a login page using the Shell.

So what is so special about a login page? Well, to state the obvious, the user should only be able to exit it after entering a correct login. Further, the user should not be able to leave the login page, i.e. navigate back to a previous page. And finally once successfully authenticated, the user should not find the login page when navigating back.

So let's see how we can capture the user on a page and then ensure that this page is no longer on the navigation stack while using Shell. So let's get going with the UI flow of a possible login experience. Our app has the following screen flow:

PageFlow

All of our pages have to be registered with the Shell. Note that the first ContentPage in the Shell.xaml file is the one getting displayed after start-up. So our Shell is structured accordingly:

<!-- Loading/Start Page -->
<ShellItem Route="loading">
    <ShellContent ContentTemplate="{DataTemplate local:LoadingPage}" />
</ShellItem>

<!-- Login and Registration Page -->
<ShellContent Route="login"
              ContentTemplate="{DataTemplate local:LoginPage}">
</ShellContent>

<!-- Main Page -->
<!-- We will get to this later -->

Our loading screen mainly simulates checking if the user has valid credentials. If the app was not a total fake on the business logic side. It might be using a Token-based flow; this is where one would check if the app still has a valid token and can go directly to the main screen or the user has to log in.

LoadingPage

Beautiful load animation, right? 😉 And here is the slim logic in the View Model :

// Called by the views OnAppearing method
public async void Init()
{
    var isAuthenticated = await this.identityService.VerifyRegistration();
    if (isAuthenticated)
    {
        await this.routingService.NavigateTo("///main");
    }
    else
    {
        await this.routingService.NavigateTo("///login");
    }
}

Note we only tell the Shell to navigate to the login screen. When using Shell, you define the kind of navigation on the target page. So for the login page, we would set it like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             ...
             Shell.PresentationMode="ModalAnimated"
             ...>
    ...
</ContentPage>

And whenever you navigate modally, it is always a good idea to override the back button navigation in the code behind of the target page as follows:

protected override bool OnBackButtonPressed()
{
    return true;
}

Why? Well because otherwise all your Android users could just simply press the Android back button and weasel their way out of your carefully crafted login process. Now let's add a registration page. Here we define the standard push navigation:

?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             ...
             Shell.PresentationMode="Animated"
             ...>
    <Shell.BackButtonBehavior>
        <BackButtonBehavior Command="{Binding ExecuteBack}"
                            TextOverride="Back" />
    </Shell.BackButtonBehavior>
    ...
</ContentPage>

Before we can navigate to the registration page, we have to register the page with the Shell. We can do this in the code behind of the AppShell.xaml.cs file:

Routing.RegisterRoute("registration", typeof(RegistrationPage));

Now we can navigate from the login page to the registration page by as follows:

Shell.Current.GoToAsync("//login/registration");

The code above implements the default navigation behaviour, i.e. a back button on the top left or by using the back button on Android. So as soon as the user has logged in, we display the main page of the app. Which is again defined in the AppShell.xaml as follows:

<!-- Main Page -->
<FlyoutItem Route="main"
            FlyoutDisplayOptions="AsMultipleItems" IsTabStop="False">
    <ShellContent Route="home"
                  IsTabStop="False"
                  ContentTemplate="{DataTemplate local:MainPage}"
                  Title="Home" />
</FlyoutItem>

Since it is not part of the login page, the Shell automatically removes the login page form the navigation stack.

So now that we are in the app. Sometimes you will want to present the user with the option to logout. Shell gives us an easy way to define a flyout menu. In Non-Shell apps, this is usually done with the MasterDetailPage. So a nice place to add our logout is as a new entry in the flyout right? Instead of defining a flyout item, it will be better to use a menu item. In general think of flyout items as areas of your app and menu items as actions in the menu. The logout is less an area and more an action. So for our logout, we define a menu item like this:

<MenuItem Text="Logout"
          Command="{Binding ExecuteLogout}" />

The Command and Binding context get defined in the code behind, i.e. the App.xaml.cs:

public AppShell()
{
    InitializeComponent();
	// ... routes and stuff
    BindingContext = this;
}

public ICommand ExecuteLogout => new Command(async () => await GoToAsync("main/login"));

Depending on the requirements of your app, you might want to force the user to log in at different times. This could be during start-up, resume or on a rainy Tuesday. Whatever the requirement, you can simply invoke the navigation similar to above, and the user will be navigated to the login screen. Once successfully logged in, the user is returned to the page on which the login page was opened.

Conclusion

With the new modal navigation mode of Shell, the implementation of a Login screen can be done quite nicely and with a lot less worrying about the state of the navigation stack. One of the main differences between using a NavigationPage compared to the Shell is how you configure the different parts within the AppShell.xaml. The Xamarin team has mentioned that more goodness should come to Shell, so be sure to stay tuned and watch out for news on the Xamarin Blog regarding new updates and features.

You can find the entire sample on GitHub. And check out David Ortinau's sample which inspired this post.

HTH

0 Comments

Icons of Xamarin & Automapper in a mobile phone

You might have already heard of AutoMapper, the library that helps you to copy Properties form Object to another written by Jimmy Bogard. Whenever you are creating a larger Xamarin Forms application, you usually end up with different models representing similar data but for different areas of your app. For example, you will get a minimalist Data Transfer Object (DTO) from your backend, which you might copy into another app-internal model or directly to the View Model representing the data displayed on your view. And this is where AutoMapper will help you out and prevent you from writing all that copy code.

I must confess - while it always felt a bit like overkill at the beginning. At the end of the project, I was still happy to have made the decision at the beginning. So let's see how we get started.

Getting Setup

The setup is quite straight forward actually add the NuGet package of AutoMapper to your Xamarin Forms project. That is until you start compiling for iOS, then it will all blow up due to some reflection issue. Since iOS is compiled Ahead Of Time (AOT), you can't do any runtime operations such as reflections. Now AutoMapper does not use reflection when running on iOS. Still, due to some weird compilation thingy issue - I haven't understood the point in detail - the compiler ends up trying to add reflection which will not work on iOS. So to make things run under iOS, we have to add the following line to our iOS csproj file:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  ...
  <ItemGroup>
    ...
    <PackageReference Include="System.Reflection.Emit" Version="4.7.0">
      <ExcludeAssets>all</ExcludeAssets>
      <IncludeAssets>none</IncludeAssets>
    </PackageReference>
  </ItemGroup>
  ...
</Project>

Then there is still the Linker under iOS that tries to remove the System.Convert assembly. Which is required by AutoMapper. But luckily we can help the linker out here by adding an XML file:

<linker>
  <assembly fullname="mscorlib">
    <type fullname="System.Convert" preserve="All" />
  </assembly>
</linker>

And setting the build property to LinkDescription:

Showing Visual Studio Properties Pane

Configuration and usage

Okay so everything is set up now we still have to tell AutoMapper what which objects we would like to copy from A to B and back again. In the small app I prepared for this blogpost we will copy the note DTO object to it's View Model counterpart. So to get a configured AutoMapper instance, we would write something like this during the start-up of the app:

public static IMapper CreateMapper()
{
    var mapperConfiguration = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Note, NoteViewModel>();
        cfg.CreateMap<NoteViewModel, Note>();
    });

    return mapperConfiguration.CreateMapper();
}jjjjjjj

Now we could convert the DTO to a View Model by invoking the AutoMapper instance as follows:

var viewModel = _mapper.Map<MainViewModel>(note);

But what I often end up doing is populating the View Model after creation or when the Page/View it is being used in is getting created. For example, an Init method on the View Model might be invoked during the OnAppearing of the View. Then we could call a service in the View Model which would return a DTO of that object. In this scenario, we will want to tell AutoMapper to map the DTO directly on the View Model itself:

public async void Init()
{
    var note = (await _noteService.GetNotes()).First();
    _mapper.Map(note, this);
}

If you had a List of View Models, i.e. a CollectionView or ListView, we would use LINQ in combination with AutoMapper to quickly convert from DTO to View Model:

Notes = (await _noteService.GetNotes())
            .Select(_mapper.Map<MainViewModel>)
            .ToList();

Having AutoMapper in place you whenever a new DTO has to be presented in your app, you would add a new configuration and then be able to use the mapping. So the more mapping you require, the quicker using AutoMapper will pay itself off.

But there is one more thing I really like when using AutoMapper in my projects, and that is testing.

Testing your mappings

"Testing? Are you serious?" - well yes, I actually am. You can check your configuration with a simple test case:

[Fact]
public void AppBoostrapper_ValidateMapping_AssertCorrectness()
{
    var mapper = AppBootstrapper.CreateMapper();
    mapper.ConfigurationProvider.AssertConfigurationIsValid();
}

This test will tell you if AutoMapper has all the information necessary to copy your data from one object to the other. So if we start adding Commands to the View Model, the mapping will fail with the information that it can not map the command into our DTO. And we obviously do not want to send an ICommand data field back to the server, so we ignore it:

cfg.CreateMap<Note, NoteViewModel>()
    .ForMember(n => n.ExecuteReset, opt => opt.Ignore())
    .ForMember(n => n.ExecuteStore, opt => opt.Ignore());
cfg.CreateMap<NoteViewModel, Note>();

But what when we add a data field WriterMood to the DTO and forget to add it to the View Model? Correct, the test will fail and inform us that we have forgotten to add the field.

Screenshot of failed AutoMapper config test

And that test has saved me from so many forgotten data fields - ahem what I meant to say it saved a friend of mine... 😅

Looking back

I will surely use AutoMapper in my future Xamarin Forms project that have their share of DTOs, internal Models and View Models since it is not only convenient to copy the properties. But it also saves me from forgetting to add properties to objects.

Be sure to check out the official documentation of AutoMapper since this post barely scratches the surface on what you can configure with AutoMapper.

As always, you can find a complete little sample application on GitHub.

HTH

0 Comments

00_TitleImage

Ever had to develop an app where the position of something was of interest. An address of a user, store, restaurant or some other point of interest? The straight forward version might be to provide the user with a simple form.

Address entry form

While the form is easy to implement and reliable, it can be cumbersome. Plus what if you want to navigate to the location? Or why not use the position of the device? Tap on a map to get the address? Well, all of these things can be achieved quite easily with a few helpful libraries provided by the Xamarin team.

Geocoding like a pro

Whenever you need to convert between coordinates and addresses, Geocoding is your best friend. You can show up with either an address or position, and you will get back the opposite:

var location = (await Geocoding.GetLocationsAsync($"{street}, {city}, {country}")).FirstOrDefault();

if (location == null) return;

Position = new Position(location.Latitude, location.Longitude);

So if we had a form where the user can enter the address and we wanted to navigate to that position, we could convert the address to geo coordinates and then, using Xamarin Essentials, open the maps app on the platform and navigate to that position.

var location = new Location(Position.Latitude, Position.Longitude);
var options = new MapLaunchOptions { NavigationMode = NavigationMode.Driving };
await Xamarin.Essentials.Map.OpenAsync(location, options);

Quite impressive for a bunch of entries and a few lines of code right?

Getting an address with one tap

But typing in addresses can be cumbersome. Why not just select the position on the map and get the address from that position? Xamarin Forms has supported the maps control for quite some time. Once you have added the NuGet package, it will allow you to present the native map platform, i.e. Google Maps on Android.

<?xml version="1.0" encoding="utf-8" ?>
             ...
             xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
             ... >

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <maps:Map x:Name="MapControl" />
        ...
    </Grid>

</ContentPage>


Note there are some setup steps required depending on the platform you intend to target. So be sure to check out the official docs from Microsoft on those details. Here we will continue to focus on the fun stuff

The map has an event MapClicked which fires when the user taps on the map with the geo-position.

Observable.FromEventPattern<MapClickedEventArgs>(
    mc => MapControl.MapClicked += mc, 
    mc => MapControl.MapClicked -= mc)
    .Subscribe(ev => ViewModel.ExecuteSetAddress.Execute(ev.EventArgs.Position));

If you are not familiar with Rx.Net, here is the equivalent code with an event handler:

protected override void OnAppearing()
{
   // ...

    MapControl.MapClicked += HandleMapClicked;
}

private void HandleMapClicked(object sender, MapClickedEventArgs e)
{
    var postion = e.Position;
    // ...
}

To give the user some feedback, we could even present the taped position with a pin:

private Unit SetPin(Position position)
{
    Pin pin = new Pin
    {
        Label = "The Place",
        Address = $"{ViewModel.Street}, {ViewModel.City}, {ViewModel.Country}",
        Type = PinType.Place,
        Position = position
    };

    var latDegrees = MapControl.VisibleRegion?.LatitudeDegrees ?? 0.01;
    var longDegrees = MapControl.VisibleRegion?.LongitudeDegrees ?? 0.01;
    MapControl.MoveToRegion(new MapSpan(position, latDegrees, longDegrees));
    MapControl?.Pins?.Clear();
    MapControl?.Pins?.Add(pin);
    return Unit.Default;
}

This code will always place the pin at the new location. If the user taps the pin, it will display the information provided to the pin.

Side note: Managing to present that info is quite a challenge given this code, 9 out of 10 times I only managed to place the pin at a new position. But perhaps that's just me

Using the geocoder, we can now get the address for those coordinates:

private async Task SetAddress(Position p)
{
    var addrs = (await Geocoding.GetPlacemarksAsync(new Location(p.Latitude, p.Longitude))).FirstOrDefault();
    Street = $"{addrs.Thoroughfare} {addrs.SubThoroughfare}";
    City = $"{addrs.PostalCode} {addrs.Locality}";
    Country = addrs.CountryName;
    // ...
}

But now you have it, going from Coordinates to an address and back the other way. Easy right? Well, there is still one more thing; the default coordinates of the map point to Rome. And I get it. Rome has a lot to offer, but if your user is not currently in Rome, it might be quite a bit of pinching and swiping until the map will be useful. So long story short, why not use the user's position?

Marco? Polo!

Getting the users position is a piece of cake again, thanks to Xamarin Essentials. That is once you have required all the permissions as described in the link. With just a few lines we can get the address and set the position:

try
{
    var location = await Geolocation.GetLastKnownLocationAsync()
	var position = new Position(location.Latitude, location.Longitude);
	Position = position;
	// ... set address and stuff
}
// many exception handlers according to docs here


The user will get prompted the first time this code runs if they want to share their position. Just as a side note if you do not want to smother the user right of the batt with permission dialogues, you might want to put some thought into when this code should first be run.

So when the view appears, we can try to get the users position and tell the map control what part of the world to display:

Geolocation.GetLastKnownLocationAsync()
            .ToObservable()
            .Catch(Observable.Return(new Location()))
            .SubscribeOn(RxApp.MainThreadScheduler)
            .Subscribe(async location => { 
                var position = new Position(location.Latitude, location.Longitude);
                Position = position;
                await SetAddress(position);
            });

I hope you could see that it does not take much to go from tiresome form to a "fun" maps control.

GifRecordingSmall

And all by using standard libraries offered to you by Microsoft. You can find the complete sample on GitHub.

HTH

0 Comments

TitleImage

A while back, I used the F# type providers to create a conversion table. That post gave me the idea if it were possible to write an app that gets its data from a website. Perhaps you have also received the request for an app. Nothing expensive and actually all the data that should be displayed is already present on the web, i.e. this website right over here. So the question is: Could such an app be created without having to write a single line of backend code?

In this blog post, I will try to create an app for one of my favourite Xamarin conferences - the Xamarin Expert Day. So let's see if we can create our Fabulous Xamarin Expert Day App.

Getting the data

Type providers are a delightful feature of F#. During compilation type providers generate the data models represented in a data source. I have written a blogpost before on the topic of parsing HTML. This time we will use another toolset that comes with the F# Data NuGet package.

Since we want to get the information about the Xamarin Experts Day conference, we can try parsing the website directly. So we could use the following line to do this:

HtmlDocument.Load "https://expertday.forxamarin.com/"

Unfortunately, we live in the modern ages of JavaScript. I don't want to go on a tangent here, but just state the fact that the Xamarin Experts Day website seems to be loading the information about the talks and the tracks after the initial HTML has been loaded. Luckily when loading the page in a browser, we get an HTML version which contains all of the information we are looking for. So instead of loading the data directly from the website, we can load the data from a file. Budget projects have their limitations... 🙃

HtmlDocument.Parse("ExpertXamarin.html")

When we look at the HTML of the website (in the browser) we can see that the speakers are listed under the following HTML structure:

<li data-speakerid="df2bc5ca-5a6b-48a9-87ac-71c817d7b240" class="sz-speaker sz-speaker--compact ">
<div class="sz-speaker__photo">
  <a href="#" onclick="return ...');">
    <img src="...894db1.jpg">
  </a>
</div>
<h3 class="sz-speaker__name">
  <a href="#" onclick="return ...');">Mark Allibone</a>
</h3>
<h4 class="sz-speaker__tagline">Lead Mobile Developer Rey Automation, Microsoft MVP</h4>
</li>

So we know that we can get the image, name, tagline and id of every speaker. So let's create a record to store that information:

type Speaker = {Id:string; Name:string; Photo:string; Tagline:string}


Creating the record is not strictly necessary. But it does make working with the data a bit easier later on. Another plus is that we could capsule the type provider code in a .Net Standard library and then share it with non F# .Net code. No, you can't access type provider data types directly from C# and while some features in C# get inspired by F#. From what I have heard, I would not hold my breath, hoping to see type providers in C# anytime soon...

With the record in place, all that is left to be done is extracting the data from the HTML. Good thing that F# Data comes along with the HTML CSS selector. The HTML CSS selector allows filtering after ids, classes and tag types. So if we wanted to get the speakers name, we can filter the component and then extract the value as follows:

// .. other parsing methods
let private getName (htmlNode:HtmlNode) =
    htmlNode.CssSelect("h3.sz-speaker__name > a") |> Seq.map (fun h -> h.DirectInnerText()) |> Seq.head

let getSpeakers (html:string) =
    HtmlDocument.Parse(html)
        .CssSelect("li.sz-speaker")
        |> Seq.map (fun s -> {Id = (getId s); Name = (getName s); Photo = (getPhoto s); Tagline = (getTagline s)})

Similarly, the rest of the data can be accessed to fill the other fields in our record. Same goes for the tracks, again we would first create a record where we store the name, time, room and speaker id of every track. We will be able to link a track to a speaker with the id:

type Track = {Room:string; Time:string; Title:string; SpeakerId:string option}

let getTracks (html:string) =
    HtmlDocument.Parse(html)
        .CssSelect("div.sz-session__card")
        |> Seq.map(fun s -> {Room = (getRoom s); Time = (getTime s); Title = (getTitle s); SpeakerId = (getSpeakerId s) })

Now that we have all the data in place, it is time to get cracking on the app.

Fabulous Xamarin Experts App

Before we start writing our UI code, there is still that shortcut we took above with loading the information out of a file. While this works great when using a script in the mobile world, this means we have to pack that HTML doc into the app. There are two approaches: either put it into the Assets folder on Android and in the Resources folder (you can also use XCAssets...) on iOS or make an Embedded Resource in the .Net Standard library. While the first option would be what Apple and Google intended you to use when adding docs, you want to ship with your app. You will have to jump through some hoops to access the document. So let's again save some time and just pack the file as an Embedded Resource in our .Net Standard project. Embedded Resources are packed into your apps binary. This results in an awkward fashion of accessing the data. While described in the official docs here, this is how it is implemented in the Xamarin Experts Day Conference App (we need a shorter name...):

let loadFile filename =
    let assembly = IntrospectionExtensions.GetTypeInfo(typedefof<Model>).Assembly;
    let stream = assembly.GetManifestResourceStream(filename);
    use streamReader = new StreamReader(stream)
    streamReader.ReadToEnd()

With that out of the way. Let's create a list of all the talks with the title, time and room. When selecting a track, we will display the Information of the presentation along with the speaker info.

So the list we can put together like this:

let showTrackCell track =
    View.ViewCell( view =
        View.StackLayout(children = [
            View.Label (text = track.Title, 
                        fontSize = FontSize 22.)
            View.Label (text = track.Time + " in " + track.Room, 
                        fontSize = FontSize 14.,
                        fontAttributes = FontAttributes.Italic)
            ]))

let view (model: Model) dispatch =

    View.ContentPage(
        content = match model.SelectedTrack with 
                    | Some track -> showTrackInfo track model dispatch
                    | None -> View.ListView(
                                    rowHeight = 80,
                                    hasUnevenRows = true,
                                    margin = Thickness(8.,0.,0.,0.),
                                    items = (model.Tracks |> List.map showTrackCell),
                                    selectionMode = ListViewSelectionMode.Single,
                                    itemSelected = (fun args -> dispatch (TrackSelected args))
                                    )
        )

And the "detail view" would be done like this:

let showTrackInfo track (model:Model) dispatch =
    let speaker = match track.SpeakerId with
                  | Some speakerId -> model.Speakers |> Seq.tryPick(fun s -> if s.Id = speakerId then Some s else None)
                  | None -> None

    let addSpeakerInfo (speaker:Speaker) =
        View.StackLayout(margin = Thickness(0.,32.,0.,0.), children = [
                View.Label (text = "Speaker", fontSize = FontSize 22. )
                View.Image (source = (Image.Path speaker.Photo))
                View.Label (text = "Presenter: " + speaker.Name)
                View.Label (text = "Tagline: " + speaker.Tagline)
            ])
        
    let speakerViewElements = match (speaker |> Option.map addSpeakerInfo) with
                              | Some speakerInfo -> speakerInfo
                              | None -> View.Label(text = "Brought to you by the Organizers");

    View.Grid (margin = Thickness(8.,8.,8.,16.),
                rowdefs = [Star; Auto],
                children = [
                    View.StackLayout(children = [
                        View.Label (text = track.Title, fontSize = FontSize 22.)
                        View.Label (text = "In: " + track.Room, fontSize = FontSize 14.)
                        View.Label (text = "At: " + track.Time, fontSize = FontSize 14., margin = Thickness(0.,-4.,0.,0.))
                        speakerViewElements
                        ])
                    (View.Button (text = "Back", command = (fun () -> dispatch (TrackSelected None)))).Row(1)
                ])

You might have noticed that the talk description is missing. The website has a JavaScript function retrieve that additional information. I think it would be possible to replicate the JavaScript call to the backend and then parse through the answer

JSON/HTML answer. But INSERT-LAME-STATEMENT-WHY-I-AM-NOT-LAZY-HERE.

The app still feels a bit ruff I think I might have to follow up in another blog post and make it pretty 😎

Conclusion

In this little experiment, we set out to see if it would be possible to write a mobile app that displays the same information as already present on a website. And while there were some bumps in the road - I am looking at you JavaScript. It was indeed possible to write an app for the Xamarin Experts Day that runs on Android and iOS.

Though I really should get started on my next post and make the app pretty 😇

You can check out the entire app on GitHub.

This post is part of theF# advent calendar. Be sure to check out the other posts.

HTH

1 Comments

pexels-photo-292426

Whether writing a mobile app with Xamarin Forms or native Xamarin.iOS/Xamarin.Android sometimes the requirements demand that your app updates as quickly as possible when something changes on the server. This might be a chat application, stock ticker or any monitoring app showing live data to a user of a process currently running in the backend.

Let's say we wanted to create a simple chat app. If we would go down the path of a traditional Create Read Update Delete (CRUD) app over HTTP, we might choose to poll the server every few seconds or so to read the latest value. While this approach delivers the results, it comes with some drawbacks. To name a few: making requests even though nothing has changed, climbing the leader board on the battery usage category plus your server gets hammered with requests only to tell them: "Sorry no news yet..." - so if not poll lets push. And this is where WebSockets come in.

The only problem with WebSockets is that the implementation in .Net is close to the metal. This results in an additional implementation effort having to be done by the developers. Luckily for .Net developers, there is SignalR which comes with all the boilerplate code you want around WebSockets. A web developer will also tell you about fall back options backed into SingalR. As a Xamarin developer, you will most probably never use those features. But the chances are good that you will be delighted by the ease of handling connections, channels or writing to specific connected clients.

SignalR is around for quite a while; it has been ported over to .Net Core and is .Net Standard compatible. This is excellent news since we can add the SignalR client directly into our Xamarin Forms app - no platform-specific/wrapper code required. But before we can start implementing our client, we will first have to create a SignalR enabled backend.

If you are new to SignalR, be aware that there is quite a big difference between SignalR and SignalR Core. If you are writing a new app today using ASP.NET Core or Azure Functions. You will want to use SignalR Core or else you will go on a nasty error hunt ending with your palm slapping against your forehead ‍🤦‍♂️

The Server

When implementing the backend, we can choose between two options. Either we implement SignalR using ASP.NET Core, or we decide to go with Azures SignalR Core Service. The later can be integrated into ASP.NET Core or an Azure Function app. The Azure option also comes with scaling capabilities - in other words, you get up to 1 Million simultaneous connections using SignalR out of the box.

For more detail on how to set up the Azure Functions and SignalR combo, you will find instructions in the official documentation.

For our simple chat application, we will want a way for our clients to send and receive messages. To achieve this, we will have to create a SignalR Hub that provides a method for sending messages:

[FunctionName("SendMessage")]
public static Task SendMessage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")]string message,
    [SignalR(HubName = "SignalRDemo")]IAsyncCollector<SignalRMessage> signalRMessages)
{
    return signalRMessages.AddAsync(
        new SignalRMessage
        {
            Target = "NewMessage",
            Arguments = new[] { message }
        });
}

On invocation, the method will send the message to all connected clients. Which brings us to our next point. Every chat participant will first have to connect to the hub so that messages can be received. So let's implement that registration method:

[FunctionName("Negotiate")]
public static SignalRConnectionInfo Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
[SignalRConnectionInfo(HubName = "SignalRDemo")] SignalRConnectionInfo connectionInfo)
{
    // connectionInfo contains an access key token with a name identifier claim set to the authenticated user
    return connectionInfo;
}

The naming of the method is a convention from SignalR. In other words, you must name the method Negotiate, or your code will not work. No, I do not want to elaborate on how I found this one out the hard way 😉

With the function and SignalR Service in place, we can now turn our focus to the client.

The mobile client

On the mobile client, we want to be able to receive messages and type responses to the group. Our simple app will have to live with the limitation of only receiving messages while being connected. At least for the moment. But here is the chat running in all of its glory.

SignalRChat

Now let's have a look at the ChatService which connects us to the backend and receives messages:

public async Task Connect()
{
    if (_connection.State == HubConnectionState.Connected) return;

    _connection.On<string>("NewMessage", (messageString) =>
    {
        var message = JsonConvert.DeserializeObject<Message>(messageString);
        _newMessage.OnNext(message);
        Debug.WriteLine(messageString);
    });

    await _connection.StartAsync();
}

Note that we register the receiver method before we connect to the backend. This way, we start receiving updates as soon as being connected to the SignalR Service. Now when implementing a receiver method, you must ensure that the type signature matches the method we defined earlier on the server. If the types or the name do not match, you will never receive any messages.

Since reading is only half the fun, let's implement the send message:

public async Task Send(Message message)
{
    var serializedPayload = JsonConvert.SerializeObject(message);

    var response = await _httpClient.PostAsync("https://gnabbersignalr.azurewebsites.net/api/SendMessage", new StringContent(serializedPayload));
    Debug.WriteLine(await response.Content.ReadAsStringAsync());
}

And if you ever had enough from the stream of messages but want to give your eyes the joy of staring at a bare app here is the disconnect method:

public async Task Disconnect()
{
    await _connection.DisposeAsync();

    _connection = new HubConnectionBuilder()
        .WithUrl(backendUrl)
        .Build();
}

I hope you could see that using SignalR it is a breeze to implement a bidirectional communication layer to your server. Which will allow your (mobile) clients to send and receive data in near real-time. Another side effect of using SignalR is that you could easily extend the app with a web client. Since your favourite JavaScript framework will allow you to use the SignalR client. If you are ready to get started with SignalR, be sure to check out the docs.

You can find the entire sample, including all the UI code on GitHub.

This blog is part of the October Xamarin Challenge. So be sure to check out the other posts for more best practices when writing Xamarin apps.

HTH