Back-End

17 jul, 2017

Xamarin Forms – Usando o Realm Mobile Database localmente

Publicidade

Neste artigo, vou mostrar como usar o banco de dados Realm para realizar a persistência de informações localmente em uma aplicação Xamarin Forms usando os no Visual Studio 2017 e a linguagem C#. Neste artigo, vou mostrar como usar o banco de dados Realm para realizar a persistência de informações localmente em uma aplicação Xamarin Forms usando os no Visual Studio 2017 e a linguagem C#.

O primeiro banco de dados que nos vem à mente quando pensamos em realizar a persistência de dados off-line em aplicações Xamarin é o SQLite. O SQLite é simples de usar, gratuito e bem popular. Isso não quer dizer que ele não tenha seus pontos negativos – e um deles é não suportar a criptografia de dados por padrão.

Existe outra opção ao SQLite ?

Sim, existe! E se chama Realm Mobile DataBase. Ele, inclusive, suporta a criptografia junto com muitos outros recursos como a definição de relacionamentos. Ele possui a versão Developer que é gratuita e funciona no Android, iOS e no Xamarin Forms.

Veja mais detalhes no site oficial e um tutorial de como usar com Xamarin.

A Plataforma Realm Mobile oferece a sincronização automática e contínua de dados em tempo real e gerenciamento de eventos entre o servidor e os dispositivos, suportando o iOS e o Android.

Uma outra vantagem do Realm é que não é necessário escrever código específico para cada plataforma para carregar o Realm. Basta incluir o pacote via Nuget em todos os projetos e ele está pronto para ser usado.

Como funciona  o Realm?

Bem, o Realms seria o equivalente a um banco de dados e contém diferentes tipos de objetos e mapeia para um arquivo no disco. Quase sempre vamos inicializar uma variável de domínio representando uma instância do banco de dados Realm usando Realm.GetInstance(string optionalPath) sem especificar o caminho. Esse método estático ai retornar uma instância Realm para a sua thread, que mapeia para um arquivo chamado default.realm, localizado em Environment.SpecialFolder.Personal.

Usar o método estático Realm.GetIntance() é maneira mais fácil de usar o Realm, mas é possível criar um objeto RealConfiguration que controla mais aspectos de como um Realm é criado. Esse objeto permite controlar o caminho do banco de dados de diversas formas (uma vez criada a configuração o caminho não pode ser alterado). Veja algumas formas de controlar o caminho do banco de dados:

  • Alterar todo o caminho passando para um novo caminho absoluto;
  • Colocar os seus arquivos do Realm em um subdiretório do local padrão, passando um caminho relativo;
  • Mudar o nome do arquivo Realm apenas passando um novo nome de arquivo;
  • Especificar a versão do schema usado.

Como o Realm implementa a interface IDispose(), suas instâncias serão fechadas automaticamente quando as variáveis saírem do escopo.

Se você precisar localizar o arquivo Realm correspondente ao banco de dados, veja este link que contém instruções detalhadas sobre como fazer isso.

Todas as alterações feitas em um objeto (adição, alteração e exclusão) devem ser feitas dentro de uma transação de gravação ou write.

Existem duas maneiras simples de criar transações de gravação:

  • Realm.BeginWrite() – Esta abordagem retorna uma transação que implementa IDispose, e você tem que dar o commit ou ela será cancelada:
using(var transaction = realm.BeginWrite()) 
{
    pessoa.Nome = "Macoratti";
    pessoa.Idade = 56;
    transaction.Commit();
}
  • Realm.Write() – Esta abordagem a transação implicíta será comitada por padrão:
realm.Write(() => 
{
    pessoa.Nome = "Macoratti";
    pessoa.Idade = 56;
});

Para excluir um objeto, passe o objeto a ser excluído para o método Realm.Remove dentro de uma transação de gravação.

var _pessoa = realm.All<Pessoa>().First(p => p.Nome == "Macoratti");
// Deleta um objeto 
using (var trans = realm.BeginWrite()) 
{
    realm.Remove(_pessoa);
    trans.Commit();
}

Agora, vamos ao exemplo onde vamos criar uma aplicação Xamarin Forms para gerenciar informações de funcionários localmente, realizando a persistência dos dados.

Recursos usados:

Criando o projeto no Visual Studio 2017 Community e definindo a página principal

Abra o Visual Studio Community 2017 e clique em New Project. Selecione Visual C#, o template Cross Plataform e, a seguir, Cross Plataform App(Xamarin.Forms or Native).

Informe o nome XF_RealmDB e clique no botão OK. A seguir, selecione Blank App e marque as opções – Xamarin.Forms e Portable Class Library (PCL) e clique em OK.

Será criado um projeto contendo no projeto Portable as páginas App.xaml e MainPage.xaml.

No code-behind do arquivo App.xaml temos a classe App.cs que irá conter o código compartilhado e que vamos usar neste artigo.

No arquivo App.cs, inclua o código a seguir, onde definimos a página principal da aplicação como sendo a página MainPage:

using Xamarin.Forms;
namespace XF_RealmDB
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            MainPage = new NavigationPage(new XF_RealmDB.MainPage());
        }
        protected override void OnStart()
        {
            // Handle when your app starts
        }
        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }
        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

Ao final, teremos as referências incluídas em todos os projetos da nossa solução. Iremos usar a página MainPage.xaml como página principal da nossa aplicação.

Incluindo pacote Realm Mobile Database no projeto

Agora, vamos incluir uma referência ao pacote do Realm Mobile DataBase via Nuget.

No menu Tools, clique em Nuget Package Manager -> Manage Nuget Packages for Solution e selecione o pacote Realm:

Selecione todos os projetos e clique no botão Install. Além do pacote Realm, também serão instaladas as dependências Realm.DataBase e Realm.DataBinding.

Definindo o modelo de domínio da aplicação: a classe Funcionario

Vamos criar uma aplicação para gerenciar informações de funcionários, logo vamos definir uma classe chamada Funcionario, que será o nosso modelo de domínio.

Crie uma pasta Model no projeto (Project-> New Folder) e, a seguir, crie a classe Funcionario.cs conforme mostra o código abaixo:

using Realms;
namespace XF_RealmDB.Model
{
    public class Funcionario : RealmObject
    {
        [PrimaryKey]
        public long Id { get; set; }
        public string Nome { get; set; }
        public string Cargo { get; set; }
        public string Setor { get; set; }
        public string Qualificacao { get; set; }
    }
}

No código, estamos usando o namespace : using Realms para que a classe possa herdar de RealmObject e poder definir a propriedade Id da classe como sendo a chave primária.

Os modelos de dados definindos usando o Realm usam classes C# tradicionais com propriedades. Basta herdar de RealmObject a classe base para criar os objetos do modelo de dados que podem ser persistido no Realm. A principal restrição é que você só pode usar um objeto na thread na qual ele foi criado, ou seja, você não pode passar o objeto para outra thread.

Definindo o código a página principal : MainPage

Agora vamos definir o código XAML da página MainPage:

<?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:local="clr-namespace:XF_RealmDB"
             x:Class="XF_RealmDB.MainPage"
             Title="Cadastro de Funcionários">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <ContentPage.Content>
        <ListView x:Name="lvFuncionarios" HasUnevenRows="false" Header="Header Value" Footer="Footer"
 ItemSelected="lvFuncrionarios_OnSelecao" >
            <ListView.HeaderTemplate>
                <DataTemplate>
                    <StackLayout Orientation="Horizontal" BackgroundColor="Blue" Padding="5,5,5,5">
                        <Label Text="Nome" FontSize="Medium" FontAttributes="Bold" TextColor="White"/>
                        <Label Text="Cargo" FontSize="Medium" FontAttributes="Bold" TextColor="White"/>
                        <Label Text="Setor" FontSize="Medium" FontAttributes="Bold" TextColor="White"/>
                    </StackLayout>
                </DataTemplate>
            </ListView.HeaderTemplate>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal" Padding="5,5,5,5">
                            <Label Text="{Binding Nome}" FontSize="Medium" />
                            <Label Text="{Binding Cargo}" FontSize="Medium" />
                            <Label Text="{Binding Setor}" FontSize="Medium" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
            <ListView.FooterTemplate>
                <DataTemplate>
                    <StackLayout Orientation="Horizontal" Padding="5,5,5,5">
                        <Button Text="Incluir Novo Funcionário" Clicked="btnIncluir_Click" />
                    </StackLayout>
                </DataTemplate>
            </ListView.FooterTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

Neste código, estamos fazendo o databinding usando as propriedades definidas na classe Funcionario e definimos dois eventos:

  • lvFuncrionarios_OnSelecao – Evento que ocorre quando um item (funcionário) do listview for selecionado;
  • btnIncluir_Click – Evento para tratar a inclusão de um novo funcionário.

Agora, vamos definir o código do arquivo MainPage.xaml.cs é mostrado a seguir:

using Realms;
using System;
using Xamarin.Forms;
using XF_RealmDB.Model;
using XF_RealmDB.Views;
namespace XF_RealmDB
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
        protected override void OnAppearing()
        {
            base.OnAppearing();
            var RealmDb = Realm.GetInstance();
            var listaFuncionarios = RealmDb.All<Funcionario>();
            lvFuncionarios.ItemsSource = listaFuncionarios;
        }
        private void lvFuncrionarios_OnSelecao(object sender, SelectedItemChangedEventArgs e)
        {
            if (e.SelectedItem == null)
            {
                //O ItemSelected é chamado quando um item é deselecionado
                //e isso torna o SelectedItem null  
                return;
            }
            var funciSelecionado = (Funcionario)e.SelectedItem;
            Navigation.PushAsync(new ExibeFunciPage(funciSelecionado));
        }
        private async void btnIncluir_Click(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new NovoFunciPage());
        }
    }
}

Neste código, temos a implementação dos eventos de seleção do Listview e de Click do botão de incluir funcionário.

O Realm salva os dados como objetos os quais estão disponíveis em um objeto estático Realm para manipulação. Estamos usando o objeto Realm no método OnAppearing para obter uma instância (GetInstance) do banco de dados e, então, chamando o método All para carregar todos os objetos Funcionario disponíveis no banco de dados.

No evento do ListView, estamos obtendo o funcionário selecionado (selectedItem), passando para o contrutor da página ExibeFunciPage() e no evento Click do Botão estamos chamando a página NovoFunciPage().

Criando as páginas para Exibir, Incluir e Editar/Excluir Funcionarios

Vamos criar uma pasta chamada Views no projeto e nesta pasta vamos incluir três páginas usando o menu Project -> Add New Item.

Selecionando Cross-Platform e a opção : Forms Blank Content Page Xaml:

Vamos criar as páginas:

  • NovoFunciPage.xaml – usada para incluir um novo funcionário;
  • ExibeFunciPage.xaml – usada para exibir os dados de um funcionário selecionado;
  • EditaFunciPage.xaml – usado para editar ou excluir um funcionário selecionado.

Definindo o código para incluir um novo funcionário : NovoFunciPage

No arquivo NovoFunciPage.xaml, inclua o código XAML abaixo para definir a interface do usuário para incluir um novo funcionário:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XF_RealmDB.Views.NovoFunciPage"
             Title="Novo Funcionário">
        <TableView Intent="Settings" BackgroundColor="White">
            <TableRoot>
                <TableSection Title="Incluir">
                    <EntryCell x:Name="txtNome" Label="Nome" Keyboard="Text" />
                    <EntryCell x:Name="txtCargo" Label="Cargo" Keyboard="Text" />
                    <EntryCell x:Name="txtSetor" Label="Setor" Keyboard="Text" />
                    <EntryCell x:Name="txtQualificacao" Label="Qualificão" Keyboard="Text" />
                    <ViewCell>
                        <ContentView Padding="0,0">
                            <ContentView.Padding>
                                <OnPlatform x:TypeArguments="Thickness" iOS="10,0" WinPhone="0,15,0,0" />
                            </ContentView.Padding>
                            <ContentView.Content>
                                <Button BackgroundColor="#fd6d6d" Text="Salvar" TextColor="White" Clicked="btnSalvar_Click" />
                            </ContentView.Content>
                        </ContentView>
                    </ViewCell>
                </TableSection>
            </TableRoot>
        </TableView>
</ContentPage>

Neste código, definimos a interface onde o usuário vai informar os dados do funcionário e clicar no botão Salvar onde definimos o evento btnSalvar_Click.

A seguir, temos o código do arquivo NovoFunciPage.xaml.cs onde tratamos o evento Click do botão e persistimos os dados do funcionário informado:

using Realms;
using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XF_RealmDB.Model;
namespace XF_RealmDB.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class NovoFunciPage : ContentPage
    {
        public NovoFunciPage()
        {
            InitializeComponent();
        }
        private void btnSalvar_Click(object sender, EventArgs e)
        {
            var RealmDb = Realm.GetInstance();
            var FunciId = RealmDb.All<Funcionario>().Count() + 1;
            var funcionario = new Funcionario()
            {
                Id = FunciId,
                Nome = txtNome.Text,
                Setor = txtSetor.Text,
                Cargo = txtCargo.Text,
                Qualificacao = txtQualificacao.Text
            };
            RealmDb.Write(() => {
                funcionario = RealmDb.Add(funcionario);
            });
            Navigation.PopAsync();
        }
    }
}

O código acima obtém uma instância do Realm Database a partir do objeto estático Realm e, a seguir, obtém o total de registros existentes incrementando o valor de 1 unidade atribuindo o valor à variável FunciId.

Depois, criamos um novo objeto Funcionario com os dados informados e persistimos os objetos usando o método Write() em uma transação segura.

Neste momento, ao executarmos o projeto, obteremos o seguinte resultado:

Na primeira imagem, das três acima, temos a página principal exibindo o ListView vazio e o botão de comando. Ainda não temos nenhum funcionário no banco de dados.

Na segunda figura, temos a página para incluir um novo funcionário. Digitamos os dados e clicamos no botão Salvar.

Na figura 3, vemos o ListView exibindo os dados (nome, cargo e setor) do funcionário incluído.

Na segunda parte do artigo vamos implementar a edição e a exclusão dos dados.