Realm Sync - SubscribeForNotifications doesn't work on a background thread

I have a .NET application that sets a bunch of listener tokens like this inside a service:

public async Task ListenForRealmChanges()
{
    var _realm = await _realmService.GetInstanceAsync();

    _realmSubscriptions = new List<IDisposable>
    {
        realm.All<Bank>().SubscribeForNotifications(async (sender, changes, error) => 
        { 
            await TellListenersUpdateCompletedAsync(); 
        });
    };    
}

I’ve been trying to get this running on a background thread (because it sometimes makes the UI thread lag) but I haven’t been able to do it. I’ve seen the documentation here and have tried using Nito.AsyncEx.Context as well as using Task.Run() but it still doesn’t update properly. What am I doing wrong in this approach?

Hi @ScribeDev,

The task itself is way too little for me to be able to help. I need to see how you start this task and more. Because, for example

Notifications only work when your realm regularly refreshes

are you doing this manually on the background thread? If so I need to see how you wrote it etc…
Additionally, the fact that the UI thread lags with async calls sounds a bit suspicious; albeit not impossible depending on what the async call does. I just hope you’re using the async/await paradigm properly.

1 Like

Hi Andrea.
The UI thread lags because in cases that there is no internet connection I need to use the synchronous GetInstance() to open the realm as the GetInstanceAsync() method doesn’t work offline. My _realmService.GetInstanceAsync() is as follows:

public async Task<Realm> GetInstanceAsync()
{
    try
    {
        SyncConfiguration config = new SyncConfiguration("Scribetech", _realmUser);
        return Connectivity.NetworkAccess == NetworkAccess.Internet ? await Realm.GetInstanceAsync(config) : Realm.GetInstance(config);
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null)
        {
            Debug.WriteLine("RealmService.GetInstanceAsync: " + ex.InnerException.Message);
        }
        else
        {
            Debug.WriteLine("RealmService.GetInstanceAsync: " + ex.Message);
        }                
        return null;
    }
}

I call the ListenForRealmChanges when I log in, from a command as follows:

public Command LoginCommand
{
    get
    {
        return new Command(async () => {

            <!-- Logic for signing in the user --!>

            await _updaterService.ListenForRealmChanges();
    });
}

I thought the point of the AsyncContext was to enable the realm to run on the background? I haven’t been able to find any examples of it being used with realm so I have previously tried wrapping the ListenForRealmChanges method in an AsyncContext but that doesn’t seem to do anything.

There are many points to address in your question. I’ll take one at a time:

  1. I’ve been trying to get this running on a background thread (because it sometimes makes the UI thread lag)

    If you’re making synchronous writes from the main thread to a synchronized Realm, there’s a chance that those writes will block the thread until a background write from Sync is finished. This is true both when using realm.Write and when using two-way databinding in Xamarin.Forms. Per our docs:

    The fact that Realm performs sync integrations on a background thread means that if you write to your realm on the main thread, there’s a small chance your UI could appear to hang as it waits for the background sync thread to finish a write transaction. Therefore, it’s a best practice never to write on the main thread when using Realm Sync.

    If you’re not perfroming synchronous writes from the main thread, then there’s no reason to believe Realm is involved in the slowdown of the UI as there’s no fundamental performance difference between GetInstance and GetInstanceAsync.

  2. I thought the point of the AsyncContext was to enable the realm to run on the background?

    Realm can work on background threads with or without AsyncContext . The reason why you might need it is if you have long-lived background threads where you want to have Realm automatically update itself or if you need to perform async work while the Realm is open. Since this seems to be what you want to achieve, you could write it like

    public Task ListenForRealmChanges() => Task.Run(() =>
    {
        AsyncContext.Run(async () =>
        {
            var _realm = await _realmService.GetInstanceAsync();
    
            _realmSubscriptions = new List<IDisposable>
            {
                realm.All<Bank>().SubscribeForNotifications(async (sender, changes, error) => 
                { 
                    await TellListenersUpdateCompletedAsync(); 
                });
            };
        });    
    });
    

    If you want to learn more about this topic, you could take a look at another similar post.

  3. realm.All<Bank>().SubscribeForNotifications(async (sender, changes, error) => 
    { 
        await TellListenersUpdateCompletedAsync(); 
    });
    

    This may not behave as expected because you’re passing an async void lambda. Because of this the SubscribeForNotifications callback will not wait for for your awaits (e.g. await TellListenersUpdateCompletedAsync(); ).

@Andrea_Catalini Thanks for your response. I have implemented your changes but SubscribeForNotifications still doesn’t trigger when I change data on the MongoDb side, wheras it does if it runs not on a background thread. Do you need more information from me? my application is a Xamarin.Forms application using Realm 10.6.0.

Hi @ScribeDev. At this point for me to assist you any further I need to take a look at your whole project. If you give me your email address, I can invite you to share your project in a private and secure cloud location.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.