Cross Platform C#
Create Responsive Xamarin Apps with ReactiveUI
Learn how to leverage the open source MVVM ReactiveUI framework, the Observer Design Pattern, ReactiveX and more to make your Xamarin apps more responsive.
- By Daniel Krzyczkowski
- 10/24/2017
Many developers, especially those who have been coding enterprise applications for years, may think they can easily shift into mobile development. That might be true for some, but the mobile world is different; there are many additional obstacles to deal with. These include unreliable Internet connections, hardware limitations and -- most important -- the need for a responsive UX.
If an app isn’t immediately responsive, users will quickly abandon it, as many studies have proven.
That’s why I decided to present a Xamarin project using the composable, functional reactive Model-View-ViewModel (MVVM) framework called ReactiveUI. I’ll start with a short introduction to the Reactive Extensions on which ReactiveUI is built, and the Observer Design Pattern.
ReactiveX
ReactiveX (often used interchangeably with the term Reactive Extensions) is an API that enables asynchronous programming with the Observer Design Pattern. It’s available for many different platforms, including Java, JavaScript, Scala and, of course, the Microsoft .NET Framework. Microsoft championed the related Reactive Extensions in 2009, providing a set of libraries for creating asynchronous and event-based applications. Last year, the Reactive Extensions for .NET project joined the .NET Foundation, and this open source technology is crucial when creating responsive UIs.
ReactiveX enables composing many different asynchronous operations and dealing with their results. You can think about ReactiveX as providing data sources that can be observed to receive proper information.
Observer Design Pattern
This pattern is based on push-based notifications between observer and provider as shown in Figure 1.
The pattern defines a provider (also called subject or observable) and zero or more observers. Observers can register with the provider and receive notifications based on predefined conditions, events or state. Once one of these triggers occurs, the provider notifies all subscribers that one of their methods has been used.
It’s especially suitable for UI applications because it provides clean separation between business logic (data sources) and the UI layer. You can read more about the Observer Design Pattern in official Microsoft documentation.
ReactiveUI -- All in One
Now that I’ve introduced ReactiveX and the Observer Design Pattern, I’ll discuss how they’re applicable to mobile application development.
Imagine an application with a list of objects (for example, cars) retrieved from a Web service and displayed to the user. The list can be filtered by color of the car or brand, but these search operations affect UX. The list should be updated dynamically without any interruption.
Another case is a login. Typically a user has to provide a username and password and then log in with the click of a button. The ReactiveUI framework provides a clean implementation to handle these cases and more.
The ReactiveUI framework is available for all .NET platforms, including Xamarin. It’s based on Reactive Extensions and, as mentioned, supports the popular MVVM architectural pattern. Now I’ll show how to use it with a sample Xamarin.Forms application.
Adding ReactiveUI to a Xamarin.Forms Project
For this article I created a simple Xamarin.Forms demo application that shows ReactiveUI in action. It consists of two pages:
- Login page with button and fields for username and password
- List page with search functionality
As shown in Figure 2, the solution consists of four projects:
- Portable Class Library (PCL) project for cross-platform business logic (the core of the solution)
- Android application project
- iOS application project
- UWP application project
To get started, remove the default MainPage file from the PCL project. Then add Views, ViewModels and Model folders, along with an Extensions folder, as shown in Figure 3.
Now it’s time to add ReactiveUI packages to the solution.
ReactiveUI is available through NuGet. Follow these steps to install it properly:
- In the PCL project:
Install-Package reactiveui-xamforms -Version 7.4.0
- In both platform projects (Android and iOS):
Install-Package reactiveui -Version 7.4.0
Install-Package reactiveui-events-xamforms -Version 7.4.0
Note that after installing all of these packages, Reactive Extensions (Rx) packages are also included, as shown in Figure 4.
Creating the Application UI
Each of the aforementioned pages, LoginPage and the subsequent CarsListViewPage, has to derive from the ContentPageBase class.
ContentPageBase is the base class for all pages that needs to be added so generic arguments can be used. Add a new ContentPageBase.cs file to the Views folder, as shown in Figure 5.
Here’s the shell of the ContentPageBase.cs code:
public class ContentPageBase<TViewModel> : ReactiveContentPage<TViewModel>
where TViewModel : class
{
}
Now that the UI is ready, the next step is to modify codebehind classes for each page.
Modifying LoginPage Code
Add a new XAML page to the Views folder called LoginPage.xaml. Its code will result in the page shown in Figure 6.
The LoginPage.xaml code is shown in Listing 1.
Note that we have to replace the default ContentPage with ContentPageBase to add generics.
Listing 1: LoginPage.xaml Code
<ui:ContentPageBase xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:ui="clr-namespace: ReactiveExtensionsWithXamarin.Core.Views; assembly=ReactiveExtensionsWithXamarin.Core"
xmlns:vms="clr-namespace:ReactiveExtensionsWithXamarin.Core.ViewModels; assembly=ReactiveExtensionsWithXamarin.Core"
x:TypeArguments="vms:LoginViewModel"
x:Class="ReactiveExtensionsWithXamarin.Core.Views.LoginPage">
<ContentPage.Content>
<StackLayout VerticalOptions="Fill" Padding="20">
<Image HeightRequest="120" WidthRequest="120" Aspect="AspectFit" Source="main_logo.png"
HorizontalOptions="Center" VerticalOptions="Start"/>
<Label Text="Xamarin with ReactiveUI!" Margin="0, 10, 0, 0" HorizontalOptions="Center"
TextColor="#8e24aa" FontAttributes="Bold" FontSize="Large"/>
<StackLayout Margin="0, 20, 0, 0">
<Label Text="Username:" FontAttributes="Bold" FontSize="Medium" TextColor="#2196f3" />
<Entry x:Name="UsernameEntry" />
<Label Text="Password:" FontAttributes="Bold" FontSize="Medium" Margin="0, 10, 0, 0"
TextColor="#2196f3" />
<Entry x:Name="PasswordEntry" IsPassword="true" />
<Button Text="Login" BackgroundColor="#2196f3" TextColor="White" x:Name="LoginButton" />
<Label x:Name="messageLabel" />
</StackLayout>
<ActivityIndicator x:Name="LoginActivityIndicator" HorizontalOptions="Center"
Margin="0, 10, 0, 0"/>
</StackLayout>
</ContentPage.Content>
</ui:ContentPageBase>
Proper namespaces and arguments should be provided:
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:ui="clr-namespace:ReactiveExtensionsWithXamarin.Core.Views;assembly=ReactiveExtensionsWithXamarin.Core"
xmlns:vms="clr-namespace:ReactiveExtensionsWithXamarin.Core.ViewModels;assembly=ReactiveExtensionsWithXamarin.Core"
x:TypeArguments="vms:LoginViewModel"
Replace existing codebehind for LoginPage.xaml.cs with the code in Listing 2.
Remember, the LoginPage class must derive from the ContentPageBase class so you can add generic arguments in the ViewModel. In this case, LoginPage will be connected with LoginViewModel.
Listing 2: LoginPage.xaml.cs Codebehind
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPageBase<LoginViewModel>
{
public LoginPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
}
protected override void OnAppearing()
{
base.OnAppearing();
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.Username, c => c.UsernameEntry.Text)
.DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Password, c => c.PasswordEntry.Text)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.LoginCommand, x => x.LoginButton.Command)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.IsLoading, x => x.LoginActivityIndicator.IsRunning)
.DisposeWith(disposables);
});
}
}
Inside the OnAppearing method you have to add bindings. The WhenActivated method is responsible for collecting all bindings in a container called CompositeDisposable. In lambda expression, this object is presented as disposables. Once a page is no longer presented, these bindings are disposed so you aren’t wasting memory resources. As you can see in Listing 2, there are bindings for UsernameEntry, PasswordEntry, LoginButton and LoginActivityIndicator.
The binding context is set to LoginViewModel:
ContentPageBase<LoginViewModel>
Modifying CarsListViewPage Code
Add a new XAML page to the Views folder called CarsListViewPage.xaml. When it’s coded up, it should look like Figure 7.
The CarsListViewPage.xaml code is presented in Listing 3.
Note that exactly like in the previous page, we have to replace the default ContentPage with ContentPageBase to add generics.
Listing 3: CarsListViewPage.xaml Code
<ui:ContentPageBase xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http:"//schemas.microsoft.com/winfx/2009/xaml
xmlns:rxui="clr-
namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:ui="clr-
namespace:ReactiveExtensionsWithXamarin.Core.Views;assembly=ReactiveExtensionsWithXam arin.Core"
xmlns:vms="clr-
namespace:ReactiveExtensionsWithXamarin.Core.ViewModels;assembly=ReactiveExtensionsWi thXamarin.Core"
x:TypeArguments="vms:CarsListViewModel"
x:Class="ReactiveExtensionsWithXamarin.Core.Views.CarsListViewPage">
<ContentPage.Content>
<StackLayout>
<Entry HorizontalOptions="FillAndExpand" Placeholder="Enter car brand..."
Margin="10, 0, 10, 10" x:Name="SearchViewEntry"/>
<ListView x:Name="CarsListView" CachingStrategy="RecycleElement"
Margin="6, 0, 6, 0">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell Text="{Binding Brand}" Detail="{Binding Model}"
ImageSource="{Binding ThumbnailUrl}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ui:ContentPageBase>