📜 ⬆️ ⬇️

Observed models in Realm Xamarin

If you are annoyed by the aspect of updating the data stored in the models, and you think about how great it would be if the model could be more independent and notify about changes, welcome to cat.



Note. Further, the story will be conducted on behalf of the author.

As a .NET developer, I’m partial to MVVM and data binding. Thanks to them, I can untie ideas from application logic, and, in most cases, write an application without having to worry about updating the user interface. However, one annoying aspect remains - updating the data stored in the models. Most often, I get a network call that takes certain data, stores it on disk, and then updates the view models by wrapping the corresponding model, whether by comparing changes or updating the entire interface. But it would be great if the model could take care of this independently and notify us about the changes, wouldn’t it? It turns out that when using Realm for long-term data storage, this becomes possible.
')
This may come as a surprise to some, but all objects stored in the Realm are initially available for observation. And even including functional dependencies and query results! And because of this, it is extremely easy to transfer them directly to data binding. In this post, I’ll focus on Xamarin.Forms, because they come with a data-binding-friendly visualization engine, but you can easily use a project with a native UI with frameworks such as MvvmCross and MVVM Light . So let's take a look at an example of how using Realm, you can create a very simple contact application with it.

Models


Since we do not want to complicate anything here, let's define only one model class - User:

public class User : RealmObject { public string Id { get; set; } = Guid.NewGuid().ToString(); public string Name { get; set; } /*    – ,   . . */ } 

Here we specify the saved object, which, thanks to Realm, will be constantly updated. So, as soon as you have an instance of the User class, there is no need to “update” it, because whenever you access the property, the current information is saved. In addition, RealmObject implements INotifyPropertyChanged , which allows you to subscribe and receive notifications about any possible changes. And although these are just a few lines of code, their impact is very significant. And even better, there are actually no templates here - there is no manual triggering of the event, SQL mapping, and, of course, the update logic.

List of contacts


First of all, the contacts application is expected to have the functionality to display, as a matter of fact, a list of contacts. In the MVVM world, this usually means providing an ObservableCollection<User> in a ViewModel , linking it, and then updating when models are changed (for example, after adding a new contact). It sounds like it is very difficult, but look at how we will cope with this task with the help of 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 here is our page (we have to set the ContactsViewModel in the code as a BindingContext , but there’s nothing interesting there, so let's just assume that 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> 

And here is the result! One-time initialization - and manual updating of Users no longer required. The runtime type of the collection returned by Realm.All implements INotifyCollectionChanged , which is monitored by the Xamarin Forms data binding mechanism, so that the interface is notified of any changes that occur in the collection. When working with a native UI project, you can either convert this type yourself, or use the AsRealmCollection extension method . Now, in confirmation of my words, let's consider how we can make certain changes.

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> 

From the moment the Entry bindings become bidirectional by default, whenever you change a username, the change is saved to disk and the page title is updated. And since our ListView on the main screen is associated with an “active” query, it will update the relevant new data already by pressing the “back” button. Connecting navigation to view models is not so fun, so I will not dwell on this here. But with one of the ways to do this can be found in the finished example .


Add contact to favorites


Let's expand a bit the functionality of our “raw” application so far, that is, we add the ability to mark a contact as a favorite. First, add a new property to our User:

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

After that, we update the ContactsViewModel so that the selected contacts are shown above, and also add the command “Switch favorites”:

 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); }); } } 

Due to this, the selected contacts will rise up, but, moreover, both groups will still be distributed in alphabetical order. And finally, let's add the ☆ button to our element (if you are interested in alternating the ☆ and ★ buttons depending on whether the contact is added to your favorites or not, see the ready example ):

 <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 all. Nothing supernatural, of course, but in this way we were able to clearly demonstrate the extent to which it is easier to add new functions when models are available for observation and are constantly updated.



Possible performance implications


This question constantly emerges when it comes to observable and active objects - “How do they affect performance?” Of course, nothing comes for free. And the ability to monitor changes is no exception. So for using this function, you have to pay a small premium in resource consumption. The good news is that this change watch only happens when you subscribe to PropertyChanged or CollectionChanged and stop as soon as the subscription stops. This means that if you have a million objects processed and you do not want to receive notifications about them, then the possibility of observing RealmObjects will not in any way affect you. And if you care about changes and use RealmObjects for data binding, then the slowdown caused by notifications will be negligible compared to the logic of data binding and layout calculations that are performed by Xamarin Forms with each update.

Conclusion


Realm allows you to easily develop the user interface of the application based on the data on the disk. Thanks to this, it is possible to develop truly capacious offline applications. And also, more importantly, create a user interface that will be independent of where the model changes occur. They can be the result of a user action, a web request, or even synchronization with a server through the Realm Mobile Platform (which will very soon join Xamarin services). And no matter what the result they will be - the user will see each update as soon as it appears.

If you have a few minutes left, take a look and pick up the database (it was created on the basis of open source code and is available for free). Or if you are like me and like to play with a ready-made project, before reading blog posts, get the code on GitHub .

Thank you for the translation.


Alexander Alekseev - Xamarin-developer, freelancer. Works with the .NET platform since 2012. Participated in the development of a procurement automation system at Digamma. C 2015 went into freelancing and switched to mobile development using Xamarin. Currently working at StecPoint on an iOS application.

It manages the XamDev.ru resource and the Xamarin Developers community in social networks: VK , Facebook , Telegram .

Other articles from our Xamarin blog can be found at #xamarincolumn .

Source: https://habr.com/ru/post/320338/


All Articles