Observable Models with Realm Xamarin

Observable Models with Realm Xamarin

As a .NET developer, I love MVVM and data binding. It allows me to decouple views from application logic and, in most cases, write my app without having to worry about updating the UI. But one annoying aspect has always been refreshing the data stored in models. In most cases, I will have a network call that will receive some data, persist it on disk, then update the view models, by wrapping the relevant model, either by diffing for changes or refreshing the whole UI altogether. Wouldn’t it be great if the model itself could take care of all that and notify us of changes? Turns out it can, if you use Realm for persistence.

It may come as a surprise to some, but all objects stored in a Realm are inherently observable. Yes, even relationships and query results! Which makes it really easy to pass them directly to data binding. In this post, I’ll focus on Xamarin.Forms since it comes with a data binding–friendly layout engine, but you could easily use a native UI project with a framework like MvvmCross or MVVM Light. So let’s see how we can use Realm and create a very basic contacts application.

Models

We want to keep it simple here, so let’s define just a single model class - a User:

Get more development news like this

public class User : RealmObject
{
    public string Id { get; set; } = Guid.NewGuid().ToString();

    public string Name { get; set; }

    /* Other relevant properties - phone numbers, addresses, etc. */
}

Here we declared a persistent object, that, thanks to Realm, will always be kept up to date. So, once you get a User instance, there’s no need to “refresh” it - whenever you access a property, you’ll get the current persisted information. Additionally, RealmObject implements INotifyPropertyChanged, which allows you to subscribe and get notifications about any changes that may occur. So, even though it’s just a few lines of code, there’s already a lot going on under the hood. And what’s even better, there’s virtually no boilerplate here - no manual event raising, no SQL mapping, and definitely no refreshing logic.

List of contacts

The most basic functionality one would expect from a contacts app is, unsurprisingly, the ability to display a list of contacts. In the MVVM world, that usually means exposing an ObservableCollection<User> in our ViewModel, binding to that, and updating it when our models change (e.g., after adding a new contact). That sounds like a lot of work, but let’s see how we’d do it with Realm:

public class ContactsViewModel
{
    private readonly Realm _realm;

    public IEnumerable<User> Users { get; }

    public ContactsViewModel()
    {
        _realm = Realm.GetInstance();
        Users = _realm.All<User>().OrderBy(u => u.Name);
    }
}

And for our page (we should set ContactsViewModel as BindingContext in the code behind, but there’s nothing interesting there, so let’s assume we did it):

<ContentPage x:Class="Contacts.ContactsPage">
    <ContentPage.Content>
        <ListView ItemsSource="{Binding Users}" x:Name="ContactsListView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Label Text="{Binding Name}"/>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

That’s it - one-time initialization and no manual refreshing of Users. The runtime type of the collection returned by Realm.All implements INotifyCollectionChanged, which the Xamarin Forms data binding engine checks for, so the UI will be notified about any changes that occur for the collection. If you’re using a Native UI project, you can either cast it yourself, or use the AsRealmCollection extension method. Now let’s see how we can perform some changes to validate my words. 🙂

Editing a single contact

public class EditContactViewModel
{
    public User User { get; }
    
    public EditContactViewModel(User user)
    {
        User = user;
    }
}

And the corresponding page:

<ContentPage x:Class="Contacts.EditContactPage"
             Title="{Binding User.Name}">
    <ContentPage.Content>
        <Entry Text="{Binding User.Name}" Placeholder="Contact name" />
    </ContentPage.Content>
</ContentPage>

Since Entry bindings are two-way by default, whenever you change the user’s name, the change is persisted on disk and the page title is updated. And because our ListView on the main screen is bound to a “live” query, it will already reflect the new state when we press the back button. Wiring the navigation between view models is not that fun, so I’ll skip it here, but you can see one way of doing it in the completed sample.

contact-edit

Favoriting a contact

Let’s add a bit more functionality to our barebones app - the ability to mark a contact as favorite. First, we’ll add a new property to our User:

public class User : RealmObject
{
    /* previous properties */
    public bool IsFavorite { get; set; }
}

Then we’ll update our ContactsViewModel to show favorites on top and we’ll add a “Toggle is favorite” command:

public class ContactsViewModel
{
    /* Other properties */

    public Command<User> ToggleIsFavoriteCommand { get; }

    public ContactsViewModel()
    {
        _realm = Realm.GetInstance();
        Users = _realm.All<User>().OrderByDescending(u => u.IsFavorite)
                                .ThenBy(u => u.Name);
        
        ToggleIsFavoriteCommand = new Command<User>(user =>
        {
            _realm.Write(() => user.IsFavorite = !user.IsFavorite);
        });
    }
}

This will move our favorites on top, while still keeping both groups ordered alphabetically. And finally, let’s add a “☆” button to our cell (if you’re curious about alternating between ☆ and ★ depending on whether the contact is favorited or not, check out the completed sample):

<ViewCell>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="60" />
        </Grid.ColumnDefinitions>
    </Grid>
    <Label Text="{Binding Name}"/>
    <Button Text="☆" Grid.Column="1"
            Command="{Binding Path=BindingContext.ToggleIsFavoriteCommand, Source={x:Reference Name=ContactsListView}}"
            CommandParameter="{Binding .}"/>
</ViewCell>

That’s it. Nothing too fancy, but it helps to demonstrate how easy it is to add new features when your models are observable and always up to date.

contact-favorite

Performance implications

A theme that always pops up when talking about observable and live objects is “How does this affect performance?”. That’s understandable - nothing comes for free, and observing changes is not an exception, so there is a very small overhead when you use this feature. The good news is that change observing only happens after you subscribe to PropertyChanged or CollectionChanged and stops when you unsubscribe. That means that if you process millions of objects and you don’t want to receive notifications about them, the observable nature of RealmObjects will not affect you in any way. And if you do care about changes and use RealmObjects for data binding, the slowdown due to notification observing is negligible compared to the data binding and layout calculation logic that Xamarin Forms performs after each update.

Conclusion

Realm makes it really easy to build your app’s UI around the data you actually have on disk. This helps you create a truly offline capable app, but more importantly, build a UI that is agnostic to where model changes originated. They can be a result of user action, web request, or even sync from the server via the Realm Mobile Platform(coming Very Soon™ to the Xamarin world) and it wouldn’t matter - the user will see all updates as soon as they arrive.

If you have a few minutes to spare, go grab the database (it’s open source & free). Or if you’re like me and like to play with the complete project before reading the blog post, grab the code on GitHub.

Next Up: Introducing the Realm Mobile Platform: Realtime Sync Plus Fully Open Source Database

General link arrow white

Nikola Irinchev

Nick has extensive experience with everything .NET - from architecting highly performant web servers to shipping cross platform mobile apps, you name it. Now he resides at Realm, delivering the best database in the world to .NET developers. When he’s not doing that, he speaks about cloud infrastructure and occasionally blogs about the hard problems he faces.