Search code examples
c#xamarinxamarin.iosxamarin.androidmvvmcross

Mvvmcross View Model navigation in Initialize doesn't work


I have a login view and another view to show after login. I'm using MvvmCross with native Xamarin.Android and Xamarin.iOS.

Therefore I have 2 viewmodels, LoginViewModel and MenuViewModel. I register the appstart like that:

RegisterAppStart<LoginViewModel>(); 

In the LoginViewModel I check if the user is already loged in. If it's logged in, I want to navigate to the MenuViewModel and stop showing the LoginViewModel.

public override async Task Initialize()
{
    await base.Initialize();
    await AppConfig.Initialize();

    bool isLoggedIn = _authService.IsUserLoggedIn();
    if (isLoggedIn)
    {
        await _navigationService.Navigate<NavControllerViewModel>();
        await _navigationService.Close(this);
    }            
}

The code above doesn't work and the LoginView is shown instead of the MenuView. What it does work, though, is the navigation after the LoginView is fully loaded and user interacts with it. So I have a command for login the user, LoginCommand, which logs in the user and if this operation is successful then I navigate to the MenuViewModel. Something like:

 private async Task Login()
 {
    if (!string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password))
    {
        if (Validations.IsValidEmail(Username))
        {
            OAuthResponse response = await UserController.Instance.Login(Username, Password);

            if (response.Error != null)
            {
                //Login NOk
                Mvx.IoCProvider.Resolve<IUserDialogs>().Alert(response.Error.Msg);
            }
            else
            {
                //Login OK
                ShowMenuScreenCommand.Execute(null);
            }
        }
        else
        {
            Mvx.IoCProvider.Resolve<IUserDialogs>().Alert("LoginView_Alert_InvalidEmail".Translate());
        }
    }
    else
    {
        Mvx.IoCProvider.Resolve<IUserDialogs>().Alert("LoginView_Alert_MissingRequiredFields".Translate());
    }
}

With the ShowMenuScreenCommand defined like that:

public IMvxAsyncCommand ShowMenuScreenCommand
{
    get
    {
        _navigateCommand = _navigateCommand ?? new MvxAsyncCommand(async () => await _navigationService.Navigate<NavControllerViewModel>());
        return _navigateCommand;
    }
}

Why the navigation in the Initialize() method doesn't work?

Is maybe the Initialize() method of the view model not a good point to do this navigation?

I've tried to put the IsUserLogedIn logic into other methods of the view lifecycle like ViewAppearing(), ViewAppeared(), etc. without success.

Thanks!


Solution

  • Instead of RegisterAppStart<LoginViewModel>();

    You can register your own MvxAppStart and check from there if the user is logged in and go directly to your NavControllerViewModel, otherwise go to the LoginViewModel. You can even inject your authentication service in the constructor.

    Create your MvxAppStart

    public class AppStart : MvxAppStart
    {
        private readonly IMvxNavigationService _navigationService;
        private readonly IAuthenticationService _authService;
    
        public AppStart(
            IMvxApplication application,
            IMvxNavigationService navigationService,
            IAuthenticationService authService)
            : base(application, navigationService)
        {
            _navigationService = navigationService ?? throw new ArgumentNullException(nameof(navigationService));
            _authService = authService ?? throw new ArgumentNullException(nameof(authService));
        }
    
        protected override async Task NavigateToFirstViewModel(object hint = null)
        {
            var isUserLoggedIn = _authService.IsUserLoggedIn();
    
            if (isUserLoggedIn)
            {
                await _navigationService.Navigate<NavControllerViewModel>();
            }
            else
            {
                await _navigationService.Navigate<LoginViewModel>();
            }
        }
    }
    

    Register your MvxAppStart

    public class App : MvxApplication
    {
        public override void Initialize()
        {
            RegisterCustomAppStart<AppStart>();
        }
    }