Search code examples
c#mvvmxamarin.androidasync-awaitmvvm-light

When to load the ViewModel for asynchronous data in a Xamarin Android app?


When can I load a viewmodel data if the service is asynchronous?

I use MVVM Light in a Xamarin Android app. The viewmodel needs to load data from SqlLite in an asynchronous operation. Something like

class MyViewModel
{
     public Task Initialize()
     {
          Records = await _database.LoadData();
     }
     ...

I am not clear when I need to call Initialize() and if I need to wait for the completion of the loading.


Solution

  • Assuming you know about ObservableCollection and the consequences of using async void (see my comments down below), I'd recommend one of these ways:

    The easier way is to simply call a InitializeAsync() method to start loading in the constructor:

    class MyViewModel
    {
        public MyViewModel()
        {
            InitializeAsync();
        }
    
        //warning: async void! 
        public async void InitializeAsync()
        {
            Records = await _database.LoadData();
        }
    }
    

    Or you could adapt the _database Service to do the lazy loading (I recommend this method, as it keeps the ViewModel clean):

    class MyViewModel
    {
        public MyViewModel()
        {
            Records = _database.LoadData();
        }
    }
    
    class Database
    {
        private ObservableCollection<Record> _data = new ObservableCollection<Record>();
        public ObservableCollection<Record> LoadData()
        {
            EnsureLoaded();
            return _data;
        }
    
        private bool _isLoaded = false;
        private async void EnsureLoaded()
        {
            lock (this)
            {
                if (_isLoaded)
                    return;
    
                _isLoaded = true;
            }
    
            //do the actual loading here
            var myResultList = await DoLoadingAsync();
            foreach (myResultList as item)
            {
                _data.Add(item);
            }
        }
    }
    

    Notes about async void:

    You can use void (instead of Task) as a return type of an asynchronous method. This will result in a "fire-and-forget" action:

    public void DoStuff()
    {
        FireAndForgetAsync();
    }
    
    private async void FireAndForgetAsync()
    {
        await Task.Delay(1000);
        throw new Exception(); //will be swallowed
    }
    

    As you are returning void, you can not await FireAndForgetAsync. You will not be able to know when the method finishes, and any exception raised inside the async void method will be swallowed.

    Notes about ObservableCollection:

    This type of List raises an event once you add / remove / replace items. You can use this events to fill your view without having to reload the list as a whole each time it changes. If you implement this correctly you can show the user each item as soon as it finished loading, instead having him to wait until everything is loaded fully.