Can a WeakReferenceMessenger message be subscribed to in a ViewModel's constructor before being navigated to?

82 views Asked by At

I'm working on a .NET MAUI app. At startup, the user is requested to insert information to be registered or to perform a login. I've decided to use the CommunityToolkit's WeakReferenceMessenger to issue a message after performing a LogIn, in order to notify the necessary pages to load content from a database.

However, I've realised the respective ViewModels' constructors - and therefore the registration of the WeakReferenceMessenger - are only called once navigated to within the application.

Therefore, the issued message that a "LogIn" has been successfully executed is correctly issued by the Messenger, but not received by e.g. the ProfileViewModel since the constructor has yet to be called.

What may I do to resolve this issue? Did I fundamentally misunderstand the use and structure of messengers in a .NET MAUI application?

1

There are 1 answers

2
Julian On

Apart from being logically wrong and technically impossible, there is no need to notify a Page or ViewModel about a login event, if said Page or ViewModel hasn't even been constructed yet.

WeakReferenceMessenger and others are used to notify existing ViewModels (or other objects) about changes they must know about, e.g. when you have a stack of pages that should update their UI without directly depending on the publisher of the message.

Any Page or ViewModel that is yet to be constructed, can check the authentication status at the time of their construction or later. What you'll need to figure out for yourself is the order in which you authenticate the user and make the different calls to the necessary pages.

One thing that you certainly could do is create some sort of service that holds information about the logged in user, such as an IUserService. Then, you can pass down this service via constructor injection and use it to check the logged in status, upon which you can decide whether or not to load certain data.

You probably have some kind of user type, e.g.:

public record AppUser(Guid Id, string Name, int Age);

Then, because you need to keep the information about the logged in user somewhere in memory anyway, create a service interface:

public interface IUserService
{
    AppUser User { get; }
    void Authenticate();
}

and an implementation for the interface:

public UserService : IUserService
{
   public AppUser User { get; private set; }

   public void Authenticate()
   {
       // perform authentication...

       User = new(Guid.NewGuid(), "John", 42);
   }
}

Then, register the service as a singleton in the ServiceCollection in your MauiProgram.cs just like you would with Pages and ViewModels:

builder.Services.AddSingleton<IUserService, UserService>();
builder.Services.AddTransient<SomePage>();
builder.Services.AddTransient<SomeViewModel>();

Then add the IUserService as a dependency to the Page or ViewModel that needs it:

public class SomeViewModel
{
    private readonly IUserService _userService;

    public SomeViewModel(IUserService userService)
    {
        _userService = userService;
    }

    public async Task LoadSomeDataAsync()
    {
        if(_userService?.User is AppUser { } user)
        {
            // load some data
        }
    }
}

and in the OnAppearing() method of some page, for example, you could then make the call to the LoadSomeDataAsync() method, which evaluates whether a user is logged in or not and then loads some data:

public partial class SomePage : ContentPage
{
    private SomeViewModel _vm;

    public SomePage(SomeViewModel vm)
    {
        _vm = vm;
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();

        await _vm.LoadSomeDataAsync();
    }
}

Now, you don't even need to send any messages, because authentication workflows often aren't an appropriate use case for that. Signing out of the application, however, is a different story. In that case, some Pages or ViewModels that depend on the authentication status may need to be notified about a logout event.