TaskLoaderCommand: a bindable AsyncCommand for Xamarin.Forms

https://github.com/roubachof/Sharpnado.TaskLoaderView

A short blog here to showcase a undocumented feature of the TaskLoaderView: the TaskLoaderCommand.
You probably read many blogs about how you should implement a AsyncCommand instead of a regular Command for your Task based code.
Of course, like all async mvvm patterns, the original idea comes from Stephen Cleary

But what if we could merge the power of TaskLoaderNotifier with the classic ICommand interface?

The TaskLoaderCommand will take as parameter a function returning a Task, and will wrap it in a NotifyTaskNotifier. You can then bind your TaskLoaderView to the NotifyTaskNotifier exposed by your TaskLoaderCommand.

In the Retronado sample, it is used for loading on demand a specific game view.

LoadOnDemandViewModel.cs

 public class LoadOnDemandViewModel : Bindable
{
    private readonly IRetroGamingService _retroGamingService;

    public LoadOnDemandViewModel(IRetroGamingService retroGamingService)
    {
        _retroGamingService = retroGamingService;

        RandomGameLoaderCommand = new TaskLoaderCommand<object, Game>(_ => GetRandomGame());
    }

    public TaskLoaderCommand<object, Game> RandomGameLoaderCommand { get; }

    private async Task<Game> GetRandomGame()
    {
        await Task.Delay(TimeSpan.FromSeconds(4));

        if (DateTime.Now.Millisecond % 2 == 0)
        {
            throw new NetworkException();
        }

        return await _retroGamingService.GetRandomGame();
    }
}

MainPage.xaml

<sharpnado:TaskLoaderView Grid.Row="2"  
                          Grid.Column="0"
                          Grid.ColumnSpan="2"
                          AccentColor="{StaticResource AccentColor}"
                          ErrorImageConverter="{StaticResource ExceptionToImageSourceConverter}"
                          ErrorMessageConverter="{StaticResource ExceptionToErrorMessageConverter}"
                          FontFamily="{StaticResource FontAtariSt}"
                          TaskLoaderNotifier="{Binding RandomGameLoaderCommand.Notifier}"
                          TaskLoaderType="ResultAsLoadingView"
                          TextColor="Black">
    <sharpnado:TaskLoaderView.NotStartedView>
        <Button AbsoluteLayout.LayoutFlags="PositionProportional"
                AbsoluteLayout.LayoutBounds="0.5, 0.5, 120, 50"
                Style="{StaticResource ButtonTextIt}"
                Command="{Binding RandomGameLoaderCommand}" />
    </sharpnado:TaskLoaderView.NotStartedView>

    <Frame  Style="{StaticResource CardStyle}"
            Margin="-15,0,-15,-15"
            Padding="0"
            skeleton:Skeleton.Animation="Fade"
            skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
            skeleton:Skeleton.IsParent="True"
            BackgroundColor="{DynamicResource CellBackgroundColor}"
            BindingContext="{Binding RandomGameLoaderCommand.Notifier}"
            CornerRadius="10"
            IsClippedToBounds="True">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="180" />
                <RowDefinition Height="40" />
                <RowDefinition Height="20" />
                <RowDefinition Height="20" />
            </Grid.RowDefinitions>
            <Image  Grid.Row="0"
                    skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
                    skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
                    Aspect="Fill"
                    Source="{Binding Result.ScreenshotUrl}" />

            <Label  Grid.Row="1"
                    Style="{StaticResource GameName}"
                    Margin="15,0"
                    skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
                    skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
                    Text="{Binding Result.Name}" />

            <Label  Grid.Row="2"
                    Style="{StaticResource GameCompany}"
                    Margin="15,0"
                    skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
                    skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
                    Text="{Binding Result.MajorCompany}" />

            <Label  Grid.Row="3"
                    Style="{StaticResource GameGenre}"
                    Margin="15,0"
                    Text="{Binding Result.MajorGenre}" />
        </Grid>
    </Frame>
</sharpnado:TaskLoaderView>  

You can see that the TaskLoaderView is simply bound to the exposed TaskLoaderNotifier of the command:

TaskLoaderNotifier="{Binding RandomGameLoaderCommand.Notifier}"  

Here the TaskLoaderView uses the special ResultAsLoadingView loader type.
It forces the TaskLoaderView to display the result content instead of the classic LoadingView while in the loading state. This is the way to go to use the Horus Xamarin.Forms.Skeleton component.

on demand gif