Clear the App memory / Unregister services / re-login within the app

I didn’t know how to describe the Title, sorry about that.

I’m using .net Maui.
And the issue I’m facing is hard for me to explain, so I will add as much code as possible, In hope to fix this issue.

I have a login and logout pages.
When the app starts I’m using this code

public App()
{
    InitializeComponent();

    try
    {
        var accountIdKey = Preferences.Get("AccountIdKey", string.Empty);
        var accountEmailKey = Preferences.Get("AccountEmailKey", string.Empty);

        if (!string.IsNullOrEmpty(Preferences.Get("FirebaseRefresher", "")) && !string.IsNullOrEmpty(accountIdKey))
        {
            var authToken = SecureStorage.GetAsync("AuthToken").Result;
            var firebaseHelper = new FirebaseHelper(accountEmailKey, authToken);

            MainPage = new AppShell();
        }
        else
        {
            MainPage = new NavigationPage(new LogInPage());
        }
    }
    catch
    {
        MainPage = new NavigationPage(new LogInPage());
    }
}

and I register services like this (in MauiProgran.cs)

builder.Services.AddSingleton<IProductService, ProductService>();
builder.Services.AddSingleton<ProductList>();
builder.Services.AddSingleton<SettingsPage>();
builder.Services.AddSingleton<ProductListViewModel>();
builder.Services.AddSingleton<SettingsViewModel>();
builder.Services.AddSingleton<AccountViewModel>();

This is how I Login (AccountViewModel)

public class AccountViewModel : BaseViewModel
{
    IAccountService accountService;
public string WebAPIKey = "my_web_api_key";
private string emailFromAuth;

public AccountViewModel()
{
}

public AccountViewModel(IAccountService accountService)
{
    this.accountService = accountService;
}

        public ICommand LogInCommand => new Command<Tuple<object, object>>(async (parameters) =>
        {
            string userEmailEntry = "";
            string userPasswordEntry = "";

            if (parameters != null && parameters.Item1 is Entry emailEntry && parameters.Item2 is Entry passwordEntry)
            {
                userEmailEntry = emailEntry.Text;
                userPasswordEntry = passwordEntry.Text;
            }

            var authProvider = new FirebaseAuthProvider(new FirebaseConfig(WebAPIKey));

            var auth = await authProvider.SignInWithEmailAndPasswordAsync(userEmailEntry.ToLower(), userPasswordEntry);
            var emailVerified = auth.User.IsEmailVerified;

            if (emailVerified)
            {
                await SecureStorage.SetAsync("AuthToken", auth.FirebaseToken);

                var firebaseClient = new FirebaseClient("https://app-name.firebasedatabase.app/",
                  new FirebaseOptions
                  {
                      AuthTokenAsyncFactory = () => Task.FromResult(auth.FirebaseToken)
                  });

                var userEmail = userEmailEntry.ToLower().Replace(".", "_");
                var userFromDB = await firebaseClient.Child(userEmail).Child("User").OnceSingleAsync<AccountProfile>();

                AccountProfile accountProfile = new();
                if (userFromDB == null)
                {
                    accountProfile = new AccountProfile
                    {
                        AccountId = Guid.NewGuid(),
                        AccountEmail = userEmailEntry.ToLower()
                    };

                    await firebaseClient.Child(userEmail).Child("User").PutAsync(accountProfile);
                }

                else
                {
                    accountProfile.AccountId = userFromDB.AccountId;
                    accountProfile.AccountEmail = userFromDB.AccountEmail;
                }

                var firebaseHelper = new FirebaseHelper(accountProfile.AccountEmail, auth.FirebaseToken);

                var content = await auth.GetFreshAuthAsync();
                var serializedcontent = JsonConvert.SerializeObject(content);
                Preferences.Set("FirebaseRefresher", serializedcontent);
                Preferences.Set("UserEmailKey", userEmailEntry.ToLower());

                App.Current.MainPage = new AppShell();
            }
            else { }
        });
}

and also, this is the FirebaseHelper

public class FirebaseHelper
{
private static FirebaseHelper instance = null;
public static FirebaseHelper Instance
{
    get
    {
        if (instance == null)
            throw new Exception("Null instance.");
        return instance;
    }
}

private string _accountEmail;
private string _authToken;
FirebaseClient firebaseClient;

public FirebaseHelper(string accountEmail, string authToken)
{
    _accountEmail = accountEmail.Replace(".", "_");
    _authToken = authToken;
    instance = this;

    firebaseClient = new FirebaseClient("https://app-name.firebasedatabase.app",
    new FirebaseOptions
    {
        AuthTokenAsyncFactory = () => Task.FromResult(_authToken)
    });
}

public void ClearInstance()
{
    instance = null;
}

public async Task<List<Product>> GetAllProductsAsync()
{
    return (await firebaseClient.Child(_accountEmail).Child("Products")
      .OnceAsync<Product>()).Select(p => p.Object).ToList();
}
}

Since I included almost my whole project… this is how I get the list

public partial class ProductListViewModel : BaseViewModel
{
    readonly IProductService productService;
    public ObservableCollection<Product> ProductList { get; set; } = new();

    public ProductListViewModel(IProductService productService)
    {
        this.productService = productService;
        (GetAllProductsCommand = new Command(async () => await GetAllProducts())).Execute(null);
    }

    public ICommand GetAllProductsCommand { get; }
    public async Task GetAllProducts()
    {
        try
        {
            var products = await productService.GetAllProductsAsync();

            if (ProductList.Count != 0)
                ProductList.Clear();

            ProductList.AddRange(products);
        }
        catch {}
    }
}

So… This is the complete way I’m logging in.

and this is my code to logout

public class SettingsViewModel
{
public ICommand LogOutCommand => new Command(async () =>
{
    Preferences.Remove("AccountIdKey", string.Empty);
    Preferences.Remove("AccountEmailKey", string.Empty);

    try
    {
        Helper.FirebaseHelper.Instance.ClearInstance();
        Preferences.Remove("FirebaseRefresher");
    }
    catch { }

    Application.Current.MainPage = new NavigationPage(new LogInPage());
});
}

Everything works almost great.
But, when I login, the app goes through (initialize) all the Viewmodels, FirebaseHelper etc…
and I get the ProductList by the login user (ill call it User1).

the problem is:
when I logout, it takes me to the “LogInPage” and if I’m logging in with different user (lets say User2), its still remembering User1 and its not going through (initialize) the Viewmodels, FirebaseHelper etc…
and its not updating the ProductList (it shows the ProductList of User1)
and also, on that code

public async Task<List<Product>> GetAllProductsAsync()
{
    return (await firebaseClient.Child(_accountEmail).Child("Products")
      .OnceAsync<Product>()).Select(p => p.Object).ToList();
}

the "_accountEmail" showing the User1 email instead of User2

and last thing, If I logout and restart the app… everything works good.

I know it’s a very long story, I just didn’t find any simple way to explain the issue.

Thank you so much.

  • the one thing that jumps out at me is that you are using Preferences.Set("UserEmailKey" but everywhere else you use AccountEmailKey. Is that correct? Otherwise the fastest way to solve this is to step through the code in the debugger and determine where it is going wrong.

    – 

  • Thank you. its ture, the Preferences.Set("UserEmailKey" is used only once for something else. but anyway I removed that just in case, but its still the same

    – 

  • Then you need to use the debugger

    – 

  • May I ask how you update your viewmodels as you register all the service and viewmodel as singleton?

    – 

  • I’m not sure how to answer that, I guess I’m not updating the viewmodels. How do I do that? or if you need more code I can post whatever needed

    – 

Leave a Comment