1
votes

Currently in my application I have two Navigation stacks.

  1. Authentication
  2. Main

My Authentication stack looks like this:

  1. Splash Page
  2. Choose Create or Login Page
  3. Login Page

After that I call:

CoreMethods.SwitchOutRootNavigation(NavigationContext.Main);

This all works fine.

When I call Logout from within the Main stack like this:

CoreMethods.SwitchOutRootNavigation(NavigationContext.Authentication);

I will currently be on "Login Page", but I really want it to be the first page "Splash Page".

Having the Navigation stacks remember the stack history is perfect for all other cases.

Question: What is the best solution for "reseting" the Authentication stack?

2
Is your splash page a xaml/C# page or activity? If its activity then this won't work. - Akash Amin

2 Answers

1
votes

What I normally do in my apps is following.

I have IAuthenticationService which has a State property, which can be LoggedIn or LoggedOut. When session state changed due to explicit login, or for instance token expires, I set the State to LoggedOut. Also I fire a broadcast message SessionStateChanged through Messenger, so I can catch this message all around the app, and react correspondingly in UI level, like change screen states and so on.

If need to completely log the user, I mean show login page when State is LoggedOut, which is your case, I do the following. I use Xamarin.Forms, but the approach would be similar if you use native iOS or Android. In my main App class (the one which derives from Xamarin.Forms.Application) I create a method call UpdateMainPage, something like this

 private async void UpdateMainPage()
    {
        if (_authService.State == SessionState.LoggedIn)
            MainPage = new NavigationPage(new RequestPage());
        else
            MainPage = new NavigationPage(new SignInPage());
    }

What happens I just change the root page of the application to SignIn flow or Main flow depending on SessionState. Then in my constructor I do the following.

 public FormsApp()
    {
        InitializeComponent();
        _authService = Mvx.Resolve<IAuthenticationService>();

        UpdateMainPage();

        var messenger = Mvx.Resolve<IMvxMessenger>();
        _sessionStateChangedToken = messenger.Subscribe<SessionStateChangedMessage>(HandleSessionStateChanged);
    }

What I need to do, I need to setup main page beforehand, then I subscribe to SessionStateChanged event, where I trigger UpdateMainPage

private void HandleSessionStateChanged(SessionStateChangedMessage sessionStateChangedMessage)
    {
        UpdateMainPage();
    }

I used this approach for several apps, and it work perfect for me. Hope this helps

1
votes

I had the very same problem recently and this is what I did:

Navigation stacks:

public enum NavigationStacks {Authentication, Main}

In the App.xaml.cs:

//Navigation stack when user is authenticated.
var mainPage = FreshPageModelResolver.ResolvePageModel<MainPageModel>();
var mainNavigation = new FreshNavigationContainer(MainPage, NavigationStacks.Main.ToString());

//Navigation stack for when user is not authenticated.
var splashScreenPage= FreshPageModelResolver.ResolvePageModel<SplashScreenPageModel>();
var authenticationNavigation = new FreshNavigationContainer(splashScreenPage, NavigationStacks.Authentication.ToString());

here you can leverage James Montemagno's Settings Plugin

if (Settings.IsUserLoggedIn)
    {
       MainPage = mainNavigation;
    }
    else
    {
       MainPage = authenticationNavigation;
    }

So far you had already done the code above. But the idea for the problem is to clear the authentication stack except the root page i.e splash Screen:

public static void PopToStackRoot(NavigationStacks navigationStack)
{
    switch (navigationStack)
    {
        case NavigationStacks.Authentication:
        {
            var mainPage = FreshPageModelResolver.ResolvePageModel<MainPageModel>();
            var mainNavigation = new FreshNavigationContainer(MainPage, NavigationStacks.Main.ToString());
            break;
        }
        case NavigationStacks.Main:
        {
            var splashScreenPage= FreshPageModelResolver.ResolvePageModel<SplashScreenPageModel>();
            var authenticationNavigation = new FreshNavigationContainer(splashScreenPage, NavigationStacks.Authentication.ToString());
            break;
        }
    }
}

And finally here is the code inside Logout command:

private void Logout()
{
    Settings.IsUserLoggedIn = false;
    NavigationService.PopToStackRoot(NavigationStacks.Authentication);  
    CoreMethods.SwitchOutRootNavigation(NavigationStacks.Authentication.ToString());
}

I know there may be better and more efficient approaches. But that worked for me.