0 Comments
  •   Posted in: 
  • F#

Photograph showing christmas gifts

So with a pandemic, Christmas was interesting, to say the least, this year. Following the COVID-19 guidelines, we were able to exchange our secret Santa gifts this year. There was only one problem. We usually have a little paper-based lottery for assigning the secret Santa. And we tend to do it on the same evening. But with COVID-19 doing its thing there was FaceTime and such involved this year. FaceTime was great but not so good when it comes to the lottery part, especially since no one should know who got who.

So what was there to be done for a successful Secret Santa 2021 without anyone knowing who picked whom? Why not do this on my own, break out Visual Studio Code and write app a little app that will do this for us. So I broke the process down into the following steps:

  • Read the input
  • Assign randomly
  • Send a text message(s)

Disclaimer: I did not receive any compensation for using Twilio. I did this because it seemed like a fun project, and SMS just seemed to be the friendliest approach for everyone involved.

Now for reading the input, I went with a more or less standard CSV file format as follows:

Person1;+15551234
Person2;+15551235

I figured this would allow for some flexibility and allow me to share the code without sharing any private contact information. 😉

Reading the input can be done with a few helpers from the .Net Framework living in the System and System.IO namespace.

type Person = { Name : string; MobileNumber : string }

let parseToPerson (inputString:string) =
    let csvItems = inputString.Split(";")
    {Name = csvItems.[0]; MobileNumber = csvItems.[1]}

Now next to the algorithm which would assign a Secret Santa. In our family, the rules are quite simple. Everyone is the Secret Santa of someone else, and no one is his or her own Secret Santa. The random assignments can are achieved by ordering the list with a random number and then zipping it to the order of the CSV file:

let random = Random()
let rec assignSecretSantas (people : Person seq) =
    let secretSantasAssignments =
        people
        |> Seq.sortBy(fun x -> random.Next())
    
    let assignments = 
        secretSantasAssignments
        |> Seq.zip people
        |> Seq.toList
    
    if assignments |> Seq.exists (fun a -> fst a = snd a) then
        assignSecretSantas people
    else
        assignments

Lastly, the assignment is checked that no one is assigned to themselves. Should there be an illegal assignment, the method will execute recursively.

Now to the part which was new to me. Send a text message from code. I have heard of Twilio before but never had the opportunity to play around with their service. Setting up an account is your run of the mill create an account scenario. They even provide you with a couple of bucks to try out their tutorials. So with my trial account and money at hand, I implemented the SMS part.

The first step is adding the NuGet package. When developing this, I used an F# Script file. Why am I mentioning this, well because since F# 5 adding/using a NuGet package in a script is as simple as adding the following lines to the top of your script file:

#r "nuget: Twilio"

open Twilio
open Twilio.Rest.Api.V2010.Account

But in the final version, I opted for a console app, which uses the standard dotnet add package Twilio to add the package to your project.

Before being able to send messages, the client needs to be initialized.

let initTwilio =
    let accountSid = configuration.GetSection("Twilio").["Sid"]
    let authToken = configuration.GetSection("Twilio").["AuthToken"]
    TwilioClient.Init(accountSid, authToken)

I must say the docs from Twilio were a great help here. Be sure to check them out if you are looking into more advanced scenarios.

With the client initialized, I wrote the code to send the message:

let secretSantaPhoneNumber = "+15551236"
let secretSantaMessage = "Testmessage"
MessageResource.Create(
    Twilio.Types.PhoneNumber(secretSantaPhoneNumber), 
    body=secretSantaMessage, 
    from=Twilio.Types.PhoneNumber("Twilio-Sender-Phone-Number"))

Sending the message was a tad more complicated than initially expected. In comparison, everything worked with my phone number, which I had to provide when signing up. When trying to send a test message to someone else in the family, I got the error message to use a number that had previously been registered with Twilio's trial version. The fix was going for a paid account - but after that, I could implement the sending of Secret Santas:

let rec informSecretSantas dryRun (santas: (Person * Person) list) =
    match santas with
    | [] -> ()
    | head::tail ->
        let santaName = (fst head).Name
        let giftReceiver = (snd head).Name
        let phoneNumber = (fst head).MobileNumber
        let body = $"The secret santa message"
        if dryRun then
            printfn "%s" body
            printfn "Sending to %s" phoneNumber
        else
            let msg = MessageResource.Create(Twilio.Types.PhoneNumber(phoneNumber), body=body, from=Twilio.Types.PhoneNumber("Twilio-Phone-Number"))
            printfn "%A" msg.Status
        informSecretSantas dryRun tail

And with that Christmas 2021 was saved. Now was this the best option? Well, there are quite a few services out there - some even support SMS and don't cost you a thing. But where would have the fun been in one of those? 🙃

For me, the experience of using F# Script files to carve out the different parts of the "app" was great and very intuitive. Running code in the F# REPL / FSI allowed for quick iterations and trying out. An added benefit was using Ionide - the plugin you want to install when developing F# with VS Code. And Ionide just got an update to support F# 5.0. 🥳

Oh, and another word of advice, if you keep the buttons pressed to execute code in the FSI - it will run multiple times. Ask how my family knows after receiving not one but four secret Santa text messages in the first "test" run. 🙈

You can find the entire code on GitHub.


Title photo by Lucie Liz from Pexels

0 Comments

00_Title

This blog post is part of the F# Advent Calendar 2020. A big thank you to Sergey for organizing this year and be sure to check out the other blog posts - after reading this one .

Based on the Model View Update (MVU) pattern the Fabulous frameworks provides the means to write functional first mobile (and desktop), clients. If you are new to Fabulous, I would recommend you check out the post by Tim (the maintainer of Fabulous) or the official docs.

The backend is implemented as an ASP.NET Core web app which has SignalR enabled. You can see how to this here.

To show how you can use SignalR with Fabulous, we will implement a chat application. And I felt like calling it "Fabulous Chat" - see how is easy it is to do fabulous things with this framework ? So let's ignore the question: "Does this world really need yet another chat app?!" in the back of our minds and let's have a look at what we will implement in this app to highlight how you could use SignalR within in a functional first mobile app.

Let's assume we have the following requirements:

  • A user should identify himself by his name.
  • The user should be able to send messages.
  • A user should be able to read messages while the app is active.

In other words, two actions are entirely UI driven, the user enters his username and then enters the chat. The user types a message and then sends it by the push of a button. However, when it comes to being able to read messages, the event will be fired from the background. Further, the SignalR connection usually is established once and then used for the remainder of the session. So let's create a module in our app, which will contain all the operations regarding the SignalR interaction.

module SignalR =
    let connectToServer =
        // connect to SignalR service

    let startListeningToChatMessages (connection: HubConnection) dispatch =
        // receive messages

    let sendMessage (connection: HubConnection) (message: ChatMessage) =
        // send message

We connect to the service after the user has provided his name. Not because it is required per se. But if at some later point we decide to add some proper authentication this will not change the flow of the app. Without further ado, let's implement the login view.

Image showing the login view

The view we use for this part is as described below.

let view model dispatch =
    View.ContentPage
        (title = "Login",
         content =
             View.StackLayout
                 (verticalOptions = LayoutOptions.Center,
                  horizontalOptions = LayoutOptions.Center,
                  children =
                      [ View.Label(text = "Please enter your Username")
                        View.Entry
                            (text = model.Username,
                             maxLength = 15,
                             placeholder = "Please enter your username",
                             completed = (fun _ -> (loginUser dispatch)),
                             textChanged = fun e -> dispatch (UsernameChanged e.NewTextValue))
                        View.Button(text = "Login", command = fun _ -> (loginUser dispatch))
                        ]))

Once the user hits the Login button, we want to establish the SignalR connection.

let connectToServer =
    let connection =
        HubConnectionBuilder()
            .WithUrl(Config.SignalRUrl)
            .WithAutomaticReconnect()
            .Build()

    async {
        do! connection.StartAsync() |> Async.AwaitTask
        return connection
    }

Since we will want to hold on to the connection, we will invoke a Command which will be evaluated in the Update method.

let update msg (model: Model) =
    match msg with
    // ... other message handling
    | Connected connection ->
        { model with
              SignalRConnection = Some connection
              AppState = Ready },
        Cmd.none
    // ... more message handling

Now, after the user is connected to the chat, we will present the chat view. This view allows the user to type a message, send it and read the responses or questions by other users connected to the service.

Image showing the chat view in action

Let's start with writing messages. Similar as with the username we again have an Entry and a button for sending the messages. Once the user sends a message, we invoke SendAsync in our SignalR module:

let sendMessage (connection: HubConnection) (message: ChatMessage) =
    async {
        let jsonMessage = JsonSerializer.Serialize(message)

        do! connection.SendAsync("SendMessage", jsonMessage)
            |> Async.AwaitTask
    }

So thus far, we have connected to the SignalR service, and we can send messages to the server. But we are still missing one essential part, and that is how we can receive messages from the backend service. What we need to do is register a listener to a specific SignalR-method (called NewMessage). We can implement our function as follows:

let startListeningToChatMessages (connection: HubConnection) dispatch =
    let handleReceivedMessage (msg: string) =
        printfn "Received message: %s" msg
        dispatch (Msg.MessageReceived(JsonSerializer.Deserialize<ChatMessage>(msg)))
        ()

    connection.On<string>("NewMessage", handleReceivedMessage)

Now in the handler method, we will dispatch a command every time a new message is received from the SignalR service. So let's extend our login function, from the beginning, to not only create a connection but also register our receiver.

let loginUser dispatch =
    async {
        let! connection = SignalR.connectToServer
        dispatch (Msg.Connected connection)

        SignalR.startListeningToChatMessages connection dispatch
        |> ignore

        dispatch (Msg.LoggedIn)
    }
    |> Async.StartImmediate

We pass in the dispatcher from the view method when registering. This allows us to dispatch a command, i.e. invoke the update method and add the new message to our list of chat messages:

let update msg (model: Model) =
    match msg with
    // ... other message handling
    | MessageReceived chatMessage ->
        { model with
              Messages = chatMessage :: model.Messages },
        Cmd.none
    // ... more message handling

And with that, the user will be able to send and receive chat messages with whoever is using the chat program at the current moment.

Conclusion

And that is how we can use SignalR in a Fabulous app and create a Fabulous Chat app. Perhaps we still have to go over the design and security to really earn that name .

What you also saw is how you can work with events that do not originate from the user's input but happen in the background. This technique can be used whenever you are using some code that gets invoked in the background while your app is doing other fabulous stuff.

You can find the complete sample of the app on GitHub.

Titlephoto by Tyler Lastovich from Pexels

0 Comments

Title image showing a rev counter gauge

Build targets are not only great to differentiate between a Debug and Release builds. You can also use them for targeting different environments or configurations of your app. Now I always like the idea of getting the best performance for apps that I put into my user's hands - in other words; I fancy to enable LLVM

Unfortunately when creating a new Target with Visual Studio 2019 (as of writing 16.5.4) the option to enable LLVM is disabled.

Showing disabled LLVM option in Visual Studio - and a screaming emoji

The issue is under consideration by the team, and for the time being, there is no way to enable LLVM via the UI Wizard in Visual Studio. Now one way to solve this is to clone your solution on to a machine running macOS and then enabling it in Visual Studio for Mac. But under Windows, the only option is to open up the csproj file and enable LLVM manually:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Stuff -->
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Gnabber|iPhone'">
    <OutputPath>bin\iPhone\Gnabber\</OutputPath>
    <DefineConstants>__IOS__;__MOBILE__;__UNIFIED__;</DefineConstants>
    <Optimize>true</Optimize>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
    <!-- Add the line bellow -->
    <MtouchUseLlvm>true</MtouchUseLlvm>
  </PropertyGroup>
  <!-- More Stuff -->
</Project>

Thanks, Victor Garcia Aprea for pointing this out to me and I hope this can be of some help to anyone out there stumbling over the same problem. If you want to check

You can find a small sample Project with a custom build target Gnabber up on GitHub.

HTH

2 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