Making ObservableCollection Thread-Safe in Xamarin.Forms

ObservableCollection is the recommended collection to use for ListViews, but it isn't thread safe. Let's explore how we can fix this and use it in our multi-threaded apps!

The Xamarin.Forms team recommends using ObservableCollection for ListView.ItemSource, but when we then try to update the collection from different threads, we'll get this error because ObservableCollection isn't thread safe:

System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index

ListProxy.get_Item (System.Int32 index) D:\a\1\s\Xamarin.Forms.Core\ListProxy.cs:129
IList.get_Item (System.Int32 index)

Luckily, the fix is pretty easy!

Solution

There is a library included in Xamarin.Forms that we can use to ensure the ObservableCollection is only updated by one thread at a time: BindingBase.EnableCollectionSynchronization.

We just need to call this method in our constructor after initializing our ObservableCollection and our collection becomes thread safe:

class MyViewModel
{
    public MyViewModel()
    {
        MyCollection = new ObservableCollection<MyModel>();
        Xamarin.Forms.BindingBase.EnableCollectionSynchronization(MyCollection, null, ObservableCollectionCallback);
    }

    public ObservableCollection<MyModel> MyCollection { get; }

    void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
    {
        // `lock` ensures that only one thread access the collection at a time
        lock (collection)
        {
            accessMethod?.Invoke();
        }
    }
}