Xamarin Forms - Gerenciando usuários no Firebase - II
 Neste artigo veremos como gerenciar dados de usuários usando o banco de dados Realtime Database do Firebase.

Continuando a primeira parte do artigo vamos iniciar a criação do projeto Xamarin Forms.

Recursos usados:

Criando o projeto Xamarin Forms

Vamos criar um novo projeto Xamarin Forms no VS 2019 Community com o nome XF_FirebaseUsuarios

Nota: Verifique se você precisa atualizar as versões do Xamarin Forms e do Xamarin.Essentials

Você pode ver como criar um projeto no Xamarin Forms neste link: Criar Projeto Xamarin Forms

A seguir vamos incluir o seguinte pacote no projeto criado:

  1. FirebaseDatabase.net

   

Esta biblioteca é um invólucro (wrapper) sobre a API REST do Firebase Realtime Database, e, dentre outros recursos, suporta a API de streaming que você pode usar para notificações em tempo real.

Veja o código fonte a documentação neste link: https://github.com/step-up-labs/firebase-database-dotnet

A seguir vamos criar as seguintes pastas no projeto compartilhado que iremos usar em nossa aplicação:

  1. Models
  2. Services
  3. ViewModels
  4. Views

No projeto Android, na pasta Resources/drawable vamos incluir o arquivo logorede1.jpg que iremos usar na página de Login.

Na pasta Models crie a classe User que representa um usuário que vai acessar nossa aplicação e que possui um nome e uma senha:

    public class User
    {
        public string Email { get; set; }
        public string Password { get; set; }
    }

Agora vamos criar a classe FirebaseUserService onde vamos implementar a funcionalidades para acessar nossa base de dados Firebase e implementar os métodos para gerenciar os usuários. Aqui vamos usar o link de referência ao nosso banco de dados Firebase Realtime Database.

Iremos usar também a referência à biblioteca Firebase.Database e Firebase.DatabaseQuery onde vamos usar classe FirebaseClient e definir os métodos:

  1. GetAllUsers()
  2. GetUser(string email)
  3. AddUser(string email, string passwd)
  4. UpdateUser(string email, string passwd)
  5. DeleteUser(string email)

Para isso crie a classe FirebaseUserService na pasta Services:

using Firebase.Database;
using Firebase.Database.Query;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using XF_FirebaseUsuarios.Models;
namespace XF_FirebaseUsuarios.Services
{
    public class FirebaseUserService
    {
        public static FirebaseClient firebase = 
            new FirebaseClient("https://xamarinfirebasedb-e9df0.firebaseio.com/");
        public static async Task<List<User>> GetAllUsers()
        {
            try
            {
                var userlist = (await firebase
                   .Child("Users")
                    .OnceAsync<User>()).Select(item =>
                            new User
                            {
                                Email = item.Object.Email,
                                Password = item.Object.Password
                            }).ToList();
                return userlist;
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Error:{e}");
                return null;
            }
        }
        public static async Task<User> GetUser(string email)
        {
            try
            {
                var allUsers = await GetAllUsers();                
                await firebase
                  .Child("Users")
                    .OnceAsync<User>();
                return allUsers.Where(a => a.Email == email).FirstOrDefault();
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Error:{e}");
                return null;
            }
        }
        public static async Task<bool> AddUser(string email, string password)
        {
            try
            {
                await firebase
                  .Child("Users")
                     .PostAsync(new User() { Email = email, Password = password });
                return true;
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Error:{e}");
                return false;
            }
        }
        public static async Task<bool> UpdateUser(string email, string password)
        {
            try
            {
                var toUpdateUser = (await firebase
                  .Child("Users")
                   .OnceAsync<User>()).Where(a => a.Object.Email == email).FirstOrDefault();
                await firebase
                  .Child("Users")
                    .Child(toUpdateUser.Key)
                      .PutAsync(new User() { Email = email, Password = password });
                return true;
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Error:{e}");
                return false;
            }
        }
        public static async Task<bool> DeleteUser(string email)
        {
            try
            {
                var toDeleteUser = (await firebase
                     .Child("Users")
                           .OnceAsync<User>()).Where(a => a.Object.Email == email).FirstOrDefault();

                await firebase.Child("Users").Child(toDeleteUser.Key).DeleteAsync();
                return true;
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Error:{e}");
                return false;
            }
        }
    }
}

Assim temos nosso serviço pronto para acessar o Firebase, e gerenciar os usuários.

Como vamos usar a abordagem MVVM vamos criar agora as seguintes view models :

Cada view model vai implementar a interface INotifyPropertyChanged() e definir a lógica para fazer o login, o registro do usuário e o gerenciamento da conta.

Para otimizar o código vamos implementar a interface INotifyPropertyChanged() em uma classe BaseViewModel e a seguir cada uma das outras view models vai herdar dessa classe.

Na pasta ViewModels vamos criar a classe BaseViewModel:

using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace XF_LancheApp.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged([CallerMemberName] string name = "") => 
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));       
    }
}

Além disso para não invocar mensagens de alerta a partir da view model usando DisplayAlert(), o que não seria uma boa prática, vamos criar um serviço de mensagens da seguinte forma:

  1. Criar a interface IMessageService na pasta Services :
    public interface IMessageService
    {
        Task AlertaAsync(string message);
    }
  1. Implementar a interface na classe MessageService na mesma pasta:
    public class MessageService : IMessageService
    {
        public async Task AlertaAsync(string message)
        {
            await App.Current.MainPage.DisplayAlert("Usuarios", message, "Ok");
        }
    }
  1. A seguir vamos injetar a dependência para o serviço no arquivo App.xaml.cs :
        public App()
        {
            DependencyService.Register<IMessageService,MessageService>();
            InitializeComponent();
            MainPage = new NavigationPage(new LoginPage());
       
        }

Agora podemos usar o serviço em cada view model inicializando o serviço no construtor e usando o método AlertAsync() como veremos a seguir.

Para concluir vamos otimizar a navegação que será feita nas view models. Podemos passar INavigation para o construtor da View Model mas isso pode ser dispendioso em termos de código se tivermos uma arquitetura de ViewModels mais aninhada.

Uma outra forma é envolver INavigation com um singleton, acessível a partir de qualquer View Model.

Vamos cria então um Singleton chamado NavigationDispatcher na pasta Services:

using System;
using Xamarin.Forms;
namespace XF_FirebaseUsuarios.Services
{
    public class NavigationDispatcher
    {
        private static NavigationDispatcher _instance;
        private INavigation _navigation;
        public static NavigationDispatcher Instance =>
                      _instance ?? (_instance = new NavigationDispatcher());
        public INavigation Navigation =>
                     _navigation ?? throw new Exception("NavigationDispatcher não foi inicializado");
        public void Initialize(INavigation navigation)
        {
            _navigation = navigation;
        }
    }
}

A seguir no arquivo App.xaml.cs vamos inicializar o serviço :

        public App()
        {
            DependencyService.Register<IMessageService,MessageService>();
            InitializeComponent();
            MainPage = new NavigationPage(new LoginPage());
            NavigationDispatcher.Instance.Initialize(MainPage.Navigation);
       
        }

A seguir basta usar o singleton na view model e os métodos de navegação.

Agora vamos aplicar esses recursos na criação das ViewModels.

Vamos iniciar criando a view model LoginViewModel  na pasta ViewModels:

using Xamarin.Forms;
using XF_FirebaseUsuarios.Services;
using XF_FirebaseUsuarios.Views;
namespace XF_FirebaseUsuarios.ViewModels
{
    public class LoginViewModel : BaseViewModel
    {
        private readonly IMessageService _messageService;
        public LoginViewModel()
        {
            _messageService = DependencyService.Get<IMessageService>();
        }
        private string email;
        public string Email
        {
            get { return email; }
            set
            {
                email = value;
                OnPropertyChanged();
            }
        }
        private string password;
        public string Password
        {
            get { return password; }
            set
            {
                password = value;
                OnPropertyChanged();
            }
        }
        public Command LoginCommand
        {
            get
            {
                return new Command(Login);
            }
        }
        public Command SignUp
        {
            get
            {
                return new Command(() =>
                {
                    NavigationDispatcher.Instance.Navigation.PushAsync(new RegisterPage());
                });
            }
        }
        private async void Login()
        {
            if (string.IsNullOrEmpty(Email) || string.IsNullOrEmpty(Password))
            {
                await _messageService.AlertaAsync("Informe o email e/ou senha");
            }
            else
            {
                var user = await FirebaseUserService.GetUser(Email);
                if (user != null)
                {
                    if (Email == user.Email && Password == user.Password)
                    {
                        await _messageService.AlertaAsync("Login feito com Sucesso");
                        await NavigationDispatcher.Instance.Navigation.PushAsync(new WelComePage(Email));
                    }
                    else
                    {
                        await _messageService.AlertaAsync("Informe o email/senha correto(s)");
                    }
                }
                else
                {
                    await _messageService.AlertaAsync("Usuário não encontrado");
                }
            }
        }
    }
}

Na pasta Views crie a seguir a ContentPage LoginPage.xaml :

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:vm="clr-namespace:XF_FirebaseUsuarios.ViewModels"
             x:Class="XF_FirebaseUsuarios.Views.LoginPage"
             BackgroundColor="WhiteSmoke"
             Title="Login de Usuário">
    <ContentPage.BindingContext>
        <vm:LoginViewModel />
    </ContentPage.BindingContext>
    <StackLayout>
        <Entry x:Name = "Email" Placeholder = "Email" 
                   PlaceholderColor="Black" 
                   Text="{Binding Email}"
                   TextColor="Black"
                   HeightRequest = "40"
                   Keyboard = "Email"/>        
        <Entry x:Name = "Password" Text="{Binding Password}" Placeholder = "Password"
                   PlaceholderColor="Black"
                   TextColor="Black"
                   HeightRequest = "40"
                   IsPassword = "True"/>        
        <Button x:Name= "loginbtn" Text = "Fazer Login " 
                    TextColor="White"
                    BackgroundColor="DarkGoldenrod"
                    Command="{Binding LoginCommand}"
                    HorizontalOptions = "FillAndExpand"/>        
        <Button x:Name="signup" Text="Registrar Usuário" 
                TextColor="White"
                BackgroundColor="DarkGoldenrod"
                Command="{Binding SignUp}" HorizontalOptions="End"/>
    </StackLayout>
</ContentPage>

Na próxima parte do artigo vamos criar as demais ViewModels e as respectivas Views.

"Dando graças ao Pai que nos fez idôneos para participar da herança dos santos na luz;
O qual nos tirou da potestade das trevas, e nos transportou para o reino do Filho do seu amor;"
Colossenses 1:12,13

Referências:


José Carlos Macoratti