Cross-Platform Development with Xamarin.Forms and Realm

Kristian Dupont is a C# developer at Realm, working on Realm Xamarin. You can find Kristian and links to his various profiles on kristiandupont.com and on Twitter

Yavor Georgiev is a developer at Realm, working on Realm Xamarin. Yavor herded iPhones at Telerik AppBuilder and bent JavaScript in unnatural shapes for NativeScript, but is now back to his .NET roots. He can be found on GitHub.

Introduction

Realm recently launched support for Xamarin and has been supporting Java, Objective-C, and Swift since 2014, as well as React Native since earlier in 2016. Realm is a mobile database that runs directly inside phones, tablets, and wearables, and offers a safe and easy non-ORM replacement for SQLite.

Realm Xamarin is built specifically for Xamarin and is designed from the ground up for reactive app development, and data binding allows you to connect your UI to your persisted data in a very smooth and seamless way. It’s not only easy to use but also heavily optimized for performance, which allows you to make apps that are more responsive and consume less power and memory.

In this blog post we’ll demonstrate how to create a basic Xamarin.Forms app backed by Realm. We will be using Xamarin Studio on OSX. If you’re using Xamarin Studio for Windows this should work as well, except you will not be able to add iOS as a target on Windows.

Setting up a Xamarin.Forms Project

Start by creating a new Xamarin.Forms project. If you want, you can download our source from GitHub, but you’ll learn more by going through the steps yourself. 😉

Choose the Xamarin.Forms App project type under Cross-platform/App in the New Project dialog.

Choose project template

Name your app, select “Use Portable Class Library” for shared code and target both Android and iOS.

Configure project

You probably want your project and solution to use the same name as your app. Put it in your preferred folder for projects and click Create!

Configure project

You now already have a basic Xamarin.Forms app – click the play button to try it out.

Adding Realm

With the foundation in place, we need to add Realm to it. Our solution has three projects, the Android app, the iOS app and the PCL library for shared code, and we’ll add Realm to all three. Do this by expanding the projects in the solution view, right-clicking on the Packages node and clicking “Add Packages…” Search for “Realm” in the search box, select it and add it. Do this for the remaining two projects as well.

Get more development news like this

Nuget dialog

Creating your model

Our demo app is a minimalistic journal app. The only model is JournalEntry, which contains a timestamp and a body text.

public class JournalEntry : RealmObject
{
    public DateTimeOffset Date { get; set; }
    public string BodyText { get; set; }
}

Setting up the UI

Now that we have our model in place we’ll create its corresponding UI. We’ll have two pages: a list of journal entries, and a details view for specific journal entries. We will use the Model-View-ViewModel (MVVM) pattern as much as possible, so every page will comprise of a View and an accompanying ViewModel.

New File Dialog

Let’s start by creating a new Page in the portable project (actually, all our code will go in the portable project). Make sure to select Forms > ContentPage with XAML in the New File dialog. Name it JournalEntriesPage. This will contain the ListView that displays the journal entries and will be the main screen of the app.

The XAML for the page should contain a ListView:

<ContentPage.Content>
    <ListView ItemsSource="{Binding Entries}" x:Name="EntriesListView">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextCell Text="{Binding Date, StringFormat='{0:dddd, MMMM d yyyy}'}" 
                          Detail="{Binding Date, StringFormat='{0:hh:mm tt}'}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage.Content>

We will set this as the MainPage of the App class that Xamarin.Forms generated for you in QuickJournal.cs:

MainPage = new NavigationPage(new JournalEntriesPage());

We’ll also need a ViewModel for our Page, so let’s create JournalEntriesViewModel.cs:

public class JournalEntriesViewModel
{
    public IEnumerable<JournalEntry> Entries { get; private set; }

    public INavigation Navigation { get; set; }
}

And wire it up in the page’s code-behind JournalEntriesPage.xaml.cs:

public JournalEntriesPage()
{
    InitializeComponent();
    BindingContext = new JournalEntriesViewModel { Navigation = Navigation };
}

You can run the app now, but you’ll end up with an empty list. Let’s do something about that!

Binding the UI to a Realm

A Realm is an object that encapsulates a database. We’ll let our ViewModel manage the realm.

private Realm _realm;

public JournalEntriesViewModel()
{
    _realm = Realm.GetInstance();

    Entries = _realm.All<JournalEntry>();
}

Note that we are exposing the available entries by making a simple query to the Realm. Realm.All<>() will give us the entire set of a given type that exists in the Realm. As it’s a “live” query, we can simply bind our list view to it and it will be continuously up to date.

Now the list should still be empty, but that’s because the database is empty. Let’s create some journal entries for testing purposes in the JournalEntriesViewModel constructor:

_realm.Write(() =>
{
    for (var i = 0; i < 3; i++)
    {
        var entry = _realm.CreateObject<JournalEntry>();
        entry.Date = DateTimeOffset.Now.AddDays(i * 7);
    }
});

Of course, this will add new entries every time we run the app, so we’ll need some way to delete entries as well. To do so, let’s add a Delete context action to the ListView, and later we’ll remove the for loop so we don’t create dummy entries when we run the app.

<TextCell.ContextActions>
    <MenuItem Text="Delete" IsDestructive="true" 
              Command="{Binding BindingContext.DeleteEntryCommand,       
              Source="{x:Reference EntriesListView}" 
              CommandParameter="{Binding .}" />
</TextCell.ContextActions>

We’ll use the command pattern to wire up interactivity between our views and view models. To do so, let’s add a new property to JournalEntriesViewModel:

public ICommand DeleteEntryCommand { get; private set; }

And initialize it in the constructor:

DeleteEntryCommand = new Command<JournalEntry>(DeleteEntry);

Here, DeleteEntry is a simple method to do the actual deletion:

private void DeleteEntry(JournalEntry entry)
{
    _realm.Write(() => _realm.Remove(entry));
}

Now we have an app that displays three journal entries straight from a Realm database using concise XAML and data binding.

Screenshot of the running app

Adding an Edit screen with Save/Cancel

Let’s add a second page that will be used to edit the body of a journal entry – JournalEntryDetailsPage:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="QuickJournal.JournalEntryDetailsPage"
             Title="{Binding Entry.Date, StringFormat='{0:MMMM d yyyy}'}">
    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Save" Command="{Binding SaveCommand}" />
    </ContentPage.ToolbarItems>
    <ContentPage.Content>
        <Editor Text="{Binding Entry.BodyText}" />
    </ContentPage.Content>
</ContentPage>

It needs to have a corresponding view model:

public class JournalEntryDetailsViewModel
{
    private Transaction _transaction;

    public JournalEntry Entry { get; private set; }
    internal INavigation Navigation { get; set; }
    public ICommand SaveCommand { get; private set; }

    public JournalEntryDetailsViewModel(JournalEntry entry, Transaction transaction)
    {
        Entry = entry;
        _transaction = transaction;
        SaveCommand = new Command(Save);
    }

    private void Save()
    {
        _transaction.Commit();
        Navigation.PopAsync(true);
    }

    internal void OnDisappearing()
    {
        _transaction.Dispose();
    }
}

And the two need to be wired up in the Page’s code-behind:

public partial class JournalEntryDetailsPage : ContentPage
{
    public JournalEntryDetailsPage(JournalEntryDetailsViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
        viewModel.Navigation = Navigation;
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        (BindingContext as JournalEntryDetailsViewModel)?.OnDisappearing();
    }
}

There are some familiar concepts here such as Command, but we introduce an important Realm feature - the Transaction. Because the details page will use data binding to edit the body of a JournalEntry directly in the Realm database, it will need to hold on to a Transaction – more on that in a bit.

Lastly, let’s add a new toolbar button to the ListView page:

<ContentPage.ToolbarItems>
    <ToolbarItem Text="Add" Command="{Binding AddEntryCommand}" />
</ContentPage.ToolbarItems> 

and a corresponding Command in the JournalEntriesViewModel:

public ICommand AddEntryCommand { get; private set; }

Be sure to remove the for loop in JournalEntriesViewModel that adds three dummy entries each time the app is launched.

We’ll need an AddEntry method to back the AddEntryCommand in JournalEntriesViewModel. It’ll be responsible for creating an entry and navigating to the details page where it can be edited:

private void AddEntry()
{
    var transaction = _realm.BeginWrite();
    var entry = _realm.CreateObject<JournalEntry>();
    entry.Date = DateTimeOffset.Now;

    var page = new JournalEntryDetailsPage(
          new JournalEntryDetailsViewModel(entry, transaction));

    Navigation.PushAsync(page);
}

There are three things of note here:

  1. We open a write transaction
  2. We create a new JournalEntry object and set its Date
  3. We create a new instance of JournayEntryDetailsPage and its view model, passing the entry and transaction we created.

That’s how you add entries. Editing existing entries is something you can try to figure out on your own, or you can cheat and look at the complete project on GitHub.

The Realm Transaction Explained

There are three mutating actions in our app: adding, editing and deleting a journal entry. One of the essential things to remember when working with Realm is that any time you want to mutate data, you must do so in a transaction.

Deleting is, as we saw, quite simple – we create a transaction quickly, remove the requested JournalEntry from the Realm, and commit the transaction. We achieve this with the Realm.Write() method. This makes sure a transaction is created and subsequently committed and disposed of (unless the statement throws an exception, that is).

To add and edit, our situation is a bit different. We want to keep a transaction alive while the user edits the body text of their journal entry. This way, we can bind the Editor directly to our BodyText property. In other words, we practically get UI-to-disk binding with no extra plumbing. The way we do this in practice is by tying the transaction to the details page. When the details page is visible, a corresponding transaction will be active.

We can easily cancel the edit by simply not committing the transaction before disposing of it. To achieve this, we let our JournalEntryDetailsViewModel own the transaction. In the Save command, we commit the transaction so the changes are persisted, and then we pop the edit page from the Xamarin.Forms navigation stack. This will trigger JournalEntryDetailsViewModel.OnDisappearing() which disposes of the transaction. If the user taps “Back” instead of “Save”, only the JournalEntryDetailsViewModel.OnDisappearing() method is called, so the transaction is disposed of without being committed first. This will cause any edits that took place inside the transaction, including creating the new entry, to be rolled back.

TL;DR

With the power of Xamarin.Forms data binding and the Realm zero-copy architecture, we can make an app that presents and persists data very quickly and with very little plumbing code.

Once you’re comfortable with writing your models so they are compatible with Realm and know how to bind them to the UI, you can create end-to-end functionality as easily as you would a mockup.

We rely on a transaction that lives together with the details page. In our demo journal app, this makes an add or an edit of a journal entry atomic, so it can easily be saved or undone. We could have chosen a different design with more fine-grained transactions, if for example we wanted to save a draft while it was being edited, but that was beyond the scope of this post.

We’ve shown you how to get the very basics running. There are obviously much more depth both to Xamarin.Forms and Realm, but we hope this will help you get started. We’ll be following up with other posts about more advanced topics, and you can learn more about Xamarin.Forms here and about Realm Xamarin here.

Next Up: Build a Realtime Application on Xamarin

General link arrow white

About the content

This content has been published here with the express permission of the author.


Kristian Dupont

4 design patterns for a RESTless mobile integration »

close