ReactiveUI with Xamarin.Forms

Every time when we start new project, especially mobile project, we face the choice of application architecture. In case of advanced applications, it’s good to choice one of common architecture patterns – MVC, MVP or MVVM. Depending on platform on which we make applications, the choice may be different but in this post we will focus on cross-platform applications in Xamarin.Forms.

One of the most common choices in Xamarin.Forms is MVVM, this is due to the small amount of work to implement it. The most important elements of MVVM:

  • Separating application logic (Views) and business logic (ViewModels),
  • Binding ViewModel properties and commands to Views visual elements,
  • Dependency Inversion with Service Locator/Dependency Injection,
  • Routing in View-First or ViewModel-First approach,
  • Message Bus to communicate between ViewModels and Views.

If we check the capabilities of Xamarin.Forms, most of these things can be achieved without additional frameworks. Xamarin.Forms Views have BindingContext which is good place to use ViewModels, all available controls allow you to bind them with BindingContext properties and commands, dependency inversion can be obtained using DependencyService and Xamarin.Forms has MessagingCenter which can be used as Message Bus. So, since most MVVM things are ready in Xamarin.Forms, why look for frameworks at all?

First of all with frameworks we can obtain routing via ViewModels. Frameworks also adds helpers and extensions that significantly simplify development and if we use ReactiveUI, we can additionally write applications in reactive manner.  Using frameworks is even more beneficial for Xamarin Native applications that don’t have such support for MVVM.

With this theory in mind, it’s time to check how ReactiveUI works in practice. To start with, create a new Xamarin.Forms project with ReactiveXamarinFormsSample name.

We have to install required libraries. In PCL project install reactiveui-xamforms and reactiveui-events-xamforms, in iOS/Android projects install reactiveui and reactiveui-core.

We have to add ViewModels folder and new file with name ViewModelBase.

public class ViewModelBase : ReactiveObject, IRoutableViewModel
{
    public string UrlPathSegment
    {
        get;
        protected set;
    }

    public IScreen HostScreen
    {
        get;
        protected set;
    }

    protected readonly CompositeDisposable subscriptionDisposables = new CompositeDisposable();

    public ViewModelBase(IScreen hostScreen = null)
    {
        HostScreen = hostScreen ?? Locator.Current.GetService<IScreen>();
    }
}

In ViewModelBase we have to add UrlPathSegment and HostScreen properties to allow routing then set HostScreen to IScreen service with Service Locator.

To handle Views we have to add Views folder and new file with name ContentPageBase.

public class ContentPageBase<TViewModel> : ReactiveContentPage<TViewModel> where TViewModel : class
{
    protected readonly CompositeDisposable SubscriptionDisposables = new CompositeDisposable();

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        SubscriptionDisposables.Clear();
    }
}

CompositeDisposable added to ContentPageBase is container that allows View to dispose all registered objects at once after disappear.

We are ready to add first View and ViewModel to our application. To do this add MainViewModel to ViewModels and new ContentPage to Views called MainView.

In MainViewModel inherit from ViewModelBase, add TextProperty and NavigateCommand.

public class MainViewModel : ViewModelBase
{
    string textProperty;
    public string TextProperty
    {
        get { return textProperty; }
        set { this.RaiseAndSetIfChanged(ref textProperty, value); }
    }

    private readonly ReactiveCommand<Unit, Unit> navigateCommand;
    public ReactiveCommand<Unit, Unit> NavigateCommand => this.navigateCommand;

    public MainViewModel(IScreen hostScreen = null) : base(hostScreen)
    {
        this.navigateCommand = ReactiveCommand.Create(() => 
        {
            HostScreen.Router.Navigate.Execute(new SecondViewModel()).Subscribe();   
        }); 
    }
}

Pay attention to RaiseAndSetIfChanged method in TextProperty setter. It informs about change in property and sets it. In constructor we set NavigateCommand to navigate to SecondViewModel.

In MainView.xaml change ContentPage to ContentPageBase, add ui, vms, TypeArguments to ContentPageBase attributes and StackLayout with Label and Button to Content.

<?xml version="1.0" encoding="UTF-8"?>
<ui:ContentPageBase xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    x:TypeArguments="vms:MainViewModel" 
    xmlns:ui="clr-namespace:ReactiveXamarinFormsSample.Views;assembly=ReactiveXamarinFormsSample" 
    xmlns:vms="clr-namespace:ReactiveXamarinFormsSample.ViewModels;assembly=ReactiveXamarinFormsSample" 
    x:Class="ReactiveXamarinFormsSample.Views.MainView">
    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        <Label x:Name="TextLabel"/>
        <Button x:Name="NavigateButton" Text="Second Page"/>
    </StackLayout>
</ui:ContentPageBase>

In MainView.xaml.cs we have to change parent class to ContentPageBase<MainViewModel> and then bind TextProperty and NavigateCommand to View.

public partial class MainView : ContentPageBase<MainViewModel>
{
    public MainView()
    {
        InitializeComponent();
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();

        NavigationPage.SetHasNavigationBar(this, false);

        this.WhenActivated(disposables =>
        {
            this.Bind(ViewModel, x => x.TextProperty, x => x.TextLabel.Text).DisposeWith(SubscriptionDisposables);
            this.BindCommand(ViewModel, x => x.NavigateCommand, x => x.NavigateButton);
        });

        ViewModel.TextProperty = "Hello Reactive World!";
    }
}

To do this, we used Bind and BindCommand setting the binding parameters accordingly.

To show navigation between ViewModels we will add SecondView and SecondViewModel. To do this create file SecondViewModel in ViewModels, inherit from ViewModelBase and add required constructor.

public class SecondViewModel : ViewModelBase
{
    public SecondViewModel(IScreen hostScreen = null) : base(hostScreen)
    {
    }
}

Now we have to update SecondView.xaml just like MainView.xaml.

<?xml version="1.0" encoding="UTF-8"?>
<ui:ContentPageBase xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    x:TypeArguments="vms:SecondViewModel"
    xmlns:ui="clr-namespace:ReactiveXamarinFormsSample.Views;assembly=ReactiveXamarinForms" 
    xmlns:vms="clr-namespace:ReactiveXamarinFormsSample.ViewModels;assembly=ReactiveXamarinForms" 
    x:Class="ReactiveXamarinFormsSample.Views.SecondView">
    <Label Text="Second View" HorizontalOptions="Center" VerticalOptions="Center" />
</ui:ContentPageBase>

In SecondView.xaml.cs we have to inherit SecondView from ContentPageBase<SecondViewModel>.

public partial class SecondView : ContentPageBase<SecondViewModel>
{
    public SecondView()
    {
        InitializeComponent();
    }
}

When we have already prepared Views and ViewModels, we just need to add navigation to navigate between Views and display initial View. Add new file AppBootstrapper with code below.

public class AppBootstrapper : ReactiveObject, IScreen
{
    public RoutingState Router { get; protected set; }

    public AppBootstrapper()
    {
        Router = new RoutingState();
        Locator.CurrentMutable.RegisterConstant(this, typeof(IScreen));
        Locator.CurrentMutable.Register(() => new MainView(), typeof(IViewFor<MainViewModel>));
        Locator.CurrentMutable.Register(() => new SecondView(), typeof(IViewFor<SecondViewModel>));

        this
            .Router
            .NavigateAndReset
            .Execute(new MainViewModel())
            .Subscribe();
    }

    public Page CreateMainPage()
    {
        return new RoutedViewHost();
    }
}

In AppBootstrapper constructor we created new RoutingState, registered MainView and SecondView with ViewModels in Locator and then set Router to navigate to initial view.

The last thing is to set result of CreateMainPage from AppBootstrapper to MainPage in App.xaml.cs.

var bootstrapper = new AppBootstrapper();
MainPage = bootstrapper.CreateMainPage();

Everything has been set and you should see following MainView when application starts. If you click Second Page Button then you should be navigated to SecondView.

It’s worth considering application architecture before it’s too late. Using MVVM keeps code in the separated layers and allows you to easily create and maintain application. Xamarin.Forms itself has many elements that make it easier to use MVVM. However, when using ReactiveUI, we additionally gain the ability to write in  reactive manner.

Example shows basics of ReactiveUI such as binding or routing. You can find more in ReactiveUI documentation, project with code from this post is on github.

What do you think about ReactiveUI? Would you use it in your project? Or maybe you already use other MVVM framework? Let me know in comment!