Tuesday, May 10, 2016

A very small pub/sub solution for Xamarin Forms

I've created a small project called TinyPubSub that only does one thing at the moment. It's made especially for use with MVVM and Xamarin Forms.

DISCLAIMER: This is a very early version of the library.

Short version: 



Long version:


I'm a big fan of vanilla MVVM. That means that I don't like to add other frameworks on top of what's already possible to achieve using only Xamarin Forms. 

A very common scenario is that I have a main page that are supposed to display some kind of data, let's say a list of ducks. I then navigate down a couple of pages in the app and find a page where I can add a duck. This should then update the first page. I could do this update in the OnAppearing event of the page but that breaks two nice rules of mine:
  1. The page should not start to load data when it is resurfaced. It should have already done that.
  2. The view is starting to handle logic. We don't want that.
So I created a small (tiny one might call it) library that solves this in the most simple way I can think of. It also handles dereferencing in a nice way. As long as you don't use Navigation.PopToRoot(); for the time being.

The features being:
  1. Simple usage
  2. Automatic un-subscription


The code

This is the structure of the project in the sample supplied with the source code (available at github).

Initialization

To initialize TinyPubSubs automatic dereference magic, just add this code to each NavigationPage you create. I'm looking at creating a forms library that adds an Init-method instead. But for now, do this.

        public App ()
        {
            // The root page of your application
            var navPage = new NavigationPage(new MainView());

            navPage.Popped += (object sender, NavigationEventArgs e) => TinyPubSub.Unsubscribe(e.Page.BindingContext);
            
            MainPage = navPage;
        }

Usage in the ViewModels

In the constructor of the ViewModels, add subscriptions like this

        public MainViewModel ()
        {
            TinyPubSub.Subscribe (this, "update-ducks", () => 
                { 
                     // Get data and update the GUI
                });    
        }

The "update-ducks" string is just a arbitrary name and you can select what ever you want. Note that I don't pass any data (yet), but that might be the next step in destroying this library by bloating it.

Publish events

In another ViewModel we then decide to add a duck. 

        public ICommand AddDuck
        {
            get {
                return new Command (async () => {
                    // Do logic and add stuff

                    // Publish an event
                    TinyPubSub.Publish ("update-ducks");
                    await Navigation.PopAsync();
                });
            }
        }

This will fire the actions (synchronous at the moment) and then pop this view. The cool part is that when we pop the view, the subscriptions will be unsubscribed (remember the code in the beginning of the post) and the memory will be cleaned up.

Summary

This is a simple way to solve a common problem. There is a built in Messaging Center in Xamarin Forms that I personally find a bit messy to use.

Future features might be async actions and passing of simple data.

5 comments:

  1. I also don't like adding bloated frameworks. Especially if they do unnecessary stuff which I'd do differently. So I read this article.

    A few thoughts about it:

    - Why don't you just bind the ducks in your MainView to a app wide list of ducks which you also add to in the AddDuck Command?
    - The NavigationPage also has a PoppedToRoot event which you could use. You just have to be careful with the NavigationEventArgs. In Popped they include the Page which was popped and in PoppedToRoot it is the Page which is remaining.
    - You should really not return a new Command in the AddDuck getter. This creates a new instance every time the property is read. Initialize the property or use a backing field instead.

    ReplyDelete
    Replies
    1. Thanks for the comment!

      1. In that specific example, you're right. That's a better way. But there might be other scenarios where you need to recalculate stuff.
      2. As I wrote, pop to root kind of destroys the automatic deregistration. I'm having a conversation with some people close to Xamarin Forms on how to handle this. I've posted a question here (http://forums.xamarin.com/discussion/66155/poptoroot#latest) as well that I'll update with what ever answer I will receive.
      3. Well, usually a viewmodel is bound once and a command once. So that will only produce more code. But in theory you are also correct here. I like the clean return new approach instead of a backing field but that's just me and I might change my mind since you're not the first to point that out. :)

      Delete
    2. You can easily write

      public ICommand AddDuck { get; } = new Command(...);

      Not to be mistaken with

      public ICommand AddDuck => new Command(...);

      The former initializes the property once, the latter is just short for defining a getter which will again be executed every time the property is read.

      Delete
    3. You are absolutely right! I totally forgot about the c# 6 candy! This is what I love about blogging. Most of the time you learn something new thanks to someone pointing out how you can do it better. Hat off to you sir!

      Delete
  2. The Borderlands series is one of the best in the lot of action gaming. Now the third installment of the series is about to release with the addition of two new varieties of items including the laser gun and I am waiting for this game to release to check it out myself. Hope it lives up to our expectations.You can check out Borderlands 3 release date

    ReplyDelete