Xamarin Forms: Persistencia de Datos con SQLite
La persistencia de datos es un aspecto fundamental en las aplicaciones móviles, ya que permite almacenar y acceder a información incluso después de que la aplicación se haya cerrado. En Xamarin Forms, una de las opciones más populares para implementar la persistencia es SQLite, una base de datos ligera y eficiente que funciona perfectamente en dispositivos móviles.
Este artículo te guiará a través de la creación de una aplicación en Xamarin Forms con persistencia de datos usando SQLite. Veremos cómo configurar SQLite en tu proyecto utilizando la biblioteca sqlite-net-pcl
y cómo realizar operaciones CRUD (Crear, Leer, Actualizar y Borrar).
Configuración del Proyecto en Visual Studio
- Abrir Visual Studio.
- Crear un nuevo proyecto: Selecciona la plantilla de Aplicación móvil (Xamarin.Forms).
- Nombre del proyecto: Asigna un nombre como
SQLiteApp
. - Plataformas: Asegúrate de seleccionar las plataformas Android e iOS.
Integrar SQLite mediante NuGet
Para integrar SQLite en tu aplicación, utilizaremos la biblioteca sqlite-net-pcl
, que es una implementación ligera de SQLite compatible con Xamarin.
- Ir al Administrador de Paquetes NuGet:
- Haz clic derecho en el proyecto compartido (
SQLiteApp
). - Selecciona Administrar paquetes NuGet.
- Haz clic derecho en el proyecto compartido (
- Buscar e instalar:
- Busca el paquete
sqlite-net-pcl
y selecciónalo. - Instálalo en los tres proyectos: Proyecto Compartido, Android, y iOS.
- Busca el paquete
Nota: Esto permitirá que SQLite se utilice en todas las plataformas de tu proyecto.
Definir el Modelo de Datos
Vamos a crear una clase Person.cs
que representará la tabla en la base de datos. Esta clase contendrá propiedades como el ID, el nombre y la edad.
Crear la clase Person.cs
en el proyecto compartido.
using SQLite;
namespace SQLiteApp.Models
{
public class Person
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
[PrimaryKey, AutoIncrement]
: Define la columna ID como clave primaria y autoincremental.Name
yAge
: Son propiedades simples que representarán las columnas de la tabla.
Configuración de la Base de Datos
Crear una Clase de Servicio para SQLite
Ahora crearemos una clase de servicio para gestionar la conexión con la base de datos. Esta clase gestionará la interacción con la base de datos SQLite de manera asíncrona y proporciona métodos para realizar operaciones CRUD sobre la tabla Person
.
Crear una clase DatabaseHelper.cs
en la carpeta Services
using SQLite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using SQLiteApp.Models;
namespace SQLiteApp.Services
{
public class DatabaseHelper
{
// Declaración de la conexión a la base de datos SQLite
private readonly SQLiteAsyncConnection _database;
// Constructor que inicializa la conexión a la base de datos y crea la tabla si no existe
public DatabaseHelper(string dbPath)
{
_database = new SQLiteAsyncConnection(dbPath);
_database.CreateTableAsync<Person>().Wait(); // Se asegura de que la tabla 'Person' está creada
}
// MÉTODO CREATE: Insertar una nueva persona en la base de datos
public Task<int> InsertPersonAsync(Person person)
{
// La inserción se realiza en la tabla Person
return _database.InsertAsync(person);
}
// MÉTODO READ: Obtener todas las personas almacenadas en la base de datos
public Task<List<Person>> GetAllPeopleAsync()
{
// Retorna una lista con todas las personas de la tabla Person
return _database.Table<Person>().ToListAsync();
}
// MÉTODO READ (por ID): Obtener una persona específica según su ID
public Task<Person> GetPersonByIdAsync(int id)
{
// Retorna la persona cuyo ID coincida con el proporcionado
return _database.Table<Person>().Where(p => p.ID == id).FirstOrDefaultAsync();
}
// MÉTODO UPDATE: Actualizar una persona en la base de datos
public Task<int> UpdatePersonAsync(Person person)
{
// Si el ID existe, se actualiza el registro correspondiente
return _database.UpdateAsync(person);
}
// MÉTODO DELETE (por objeto): Eliminar una persona de la base de datos
public Task<int> DeletePersonAsync(Person person)
{
// Elimina la persona que se pasa como parámetro
return _database.DeleteAsync(person);
}
// MÉTODO DELETE (por ID): Eliminar una persona según su ID
public Task<int> DeletePersonByIdAsync(int id)
{
// Recupera la persona por ID y luego la elimina
return _database.DeleteAsync<Person>(id);
}
// MÉTODO SEARCH: Buscar personas según su nombre (similar a una consulta de búsqueda)
public Task<List<Person>> SearchPeopleByNameAsync(string name)
{
// Busca personas cuyo nombre contenga la cadena proporcionada
return _database.Table<Person>()
.Where(p => p.Name.ToLower().Contains(name.ToLower()))
.ToListAsync();
}
// MÉTODO COUNT: Contar el número total de personas en la base de datos
public Task<int> GetPeopleCountAsync()
{
// Retorna el número total de registros en la tabla Person
return _database.Table<Person>().CountAsync();
}
// MÉTODO CLEAR: Borrar todos los registros de la tabla
public Task<int> ClearAllPeopleAsync()
{
// Borra todos los registros de la tabla Person
return _database.DeleteAllAsync<Person>();
}
}
}
-
Constructor (
DatabaseHelper(string dbPath)
):- Inicializa la conexión con la base de datos a través de la clase
SQLiteAsyncConnection
. - Usa
CreateTableAsync<Person>()
para asegurarse de que la tabla Person esté creada cuando se inicializa la clase. Si la tabla ya existe, no se vuelve a crear.
- Inicializa la conexión con la base de datos a través de la clase
-
InsertPersonAsync(Person person)
:- Operación: Crear.
- Inserta un nuevo registro en la tabla
Person
. - Retorna un
Task<int>
que indica cuántas filas fueron afectadas. Si la operación fue exitosa, se devolverá el valor 1.
-
GetAllPeopleAsync()
:- Operación: Leer (Read).
- Recupera y retorna una lista con todas las personas almacenadas en la tabla
Person
. - Utiliza el método
ToListAsync()
para obtener el resultado de forma asíncrona.
-
GetPersonByIdAsync(int id)
:- Operación: Leer (Read).
- Recupera una persona específica según el valor de su ID.
- Utiliza la cláusula
Where
para filtrar el registro con el ID proporcionado y retorna el primer resultado coincidente onull
si no se encuentra.
-
UpdatePersonAsync(Person person)
:- Operación: Actualizar (Update).
- Actualiza un registro existente en la base de datos. El método usa el ID de la persona para identificar cuál registro actualizar.
- Este método es útil cuando necesitas modificar un dato existente, como cambiar el nombre o la edad.
-
DeletePersonAsync(Person person)
:- Operación: Eliminar (Delete).
- Elimina un registro específico de la tabla
Person
utilizando el objeto de la persona como referencia. El método devuelve unTask<int>
que indica cuántas filas fueron eliminadas (si el valor es 1, significa que la eliminación fue exitosa).
-
DeletePersonByIdAsync(int id)
:- Operación: Eliminar (Delete).
- Similar al método anterior, pero este utiliza el ID de la persona en lugar del objeto completo. Es útil si solo tienes el ID y no el objeto completo.
-
SearchPeopleByNameAsync(string name)
:- Operación: Leer (Search).
- Realiza una búsqueda en la tabla
Person
para encontrar todas las personas cuyo nombre contenga el texto especificado (búsqueda parcial). - Utiliza la función
ToLower()
para que la búsqueda sea insensible a mayúsculas/minúsculas.
-
GetPeopleCountAsync()
:- Operación: Leer (Count).
- Retorna la cantidad de registros en la tabla
Person
. - Este método es útil para mostrar información como “Número total de personas almacenadas”.
-
ClearAllPeopleAsync()
:- Operación: Eliminar (Clear All).
- Elimina todos los registros de la tabla
Person
sin eliminar la estructura de la tabla. - Útil para reiniciar la tabla en situaciones como pruebas o restablecimientos de datos.
Explicación de los Componentes Clave
-
SQLiteAsyncConnection
:- Esta clase de SQLite maneja la conexión con la base de datos y permite realizar operaciones de manera asíncrona, lo que es esencial para no bloquear la interfaz de usuario mientras se ejecutan operaciones de base de datos.
-
Task
:- Todos los métodos son asíncronos y retornan objetos
Task
. Esto es fundamental en aplicaciones móviles para evitar bloquear el hilo principal mientras se interactúa con la base de datos, permitiendo una experiencia de usuario más fluida.
- Todos los métodos son asíncronos y retornan objetos
-
Métodos de CRUD:
- Los métodos
Insert
,Update
,Delete
, yGet
siguen el patrón básico de operaciones CRUD (Crear, Leer, Actualizar, Eliminar), permitiendo manipular los datos almacenados de manera eficiente.
- Los métodos
-
Cláusula
Where
:- El método
Where
permite filtrar resultados basados en una condición. En este caso, se utiliza para buscar registros por ID o por nombre.
- El método
Definir la Ubicación de la Base de Datos
La ubicación de la base de datos es crucial para que tu aplicación pueda leer y escribir datos correctamente. En Xamarin Forms, la ruta de la base de datos puede variar entre plataformas (Android e iOS). A continuación, te mostraré cómo definir y configurar correctamente la ubicación de la base de datos para cada plataforma.
En Android
En Android, la base de datos se guarda en un directorio específico dentro del almacenamiento de la aplicación. La ruta de este directorio es accesible a través de la ruta System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)
.
Obtener la Ruta en Android:
-
Abrir
MainActivity.cs
: Este archivo se encuentra en el proyecto de Android dentro dePlatforms/Android
. -
Definir la Ruta de la Base de Datos: Añade el siguiente código en el método
OnCreate
deMainActivity.cs
.
using Android.App;
using Android.Content.PM;
using Android.OS;
using System.IO;
[Activity(Label = "SQLiteApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
// Definir la ruta de la base de datos
string dbName = "people.db3";
string dbPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), dbName);
// Inicializar la aplicación Xamarin.Forms
LoadApplication(new App(dbPath));
}
}
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)
: Obtiene la ruta al directorio personal de la aplicación, que es adecuado para almacenar la base de datos.Path.Combine
: Combina la ruta del directorio personal con el nombre del archivo de la base de datos (people.db3
).
En iOS
En iOS, la base de datos se guarda en un directorio dentro del espacio de documentos de la aplicación. La ruta se obtiene a través del método Environment.GetFolderPath
.
Obtener la Ruta en iOS:
-
Abrir
AppDelegate.cs
: Este archivo se encuentra en el proyecto de iOS dentro dePlatforms/iOS
. -
Definir la Ruta de la Base de Datos: Añade el siguiente código en el método
FinishedLaunching
deAppDelegate.cs
.
using Foundation;
using UIKit;
using System.IO;
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// Definir la ruta de la base de datos
string dbName = "people.db3";
string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "..", "Library", dbName);
// Inicializar la aplicación Xamarin.Forms
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App(dbPath));
return base.FinishedLaunching(application, launchOptions);
}
}
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
: Obtiene la ruta al directorio de documentos de la aplicación.Path.Combine
: Combina la ruta de documentos con"Library"
para obtener una ruta adecuada para la base de datos en iOS.
Configuración en el Proyecto Compartido
Importante: El constructor de la Clase
App.xaml.cs
, recibe la ruta de la base de datos como parámetro (dbPath
) y la pasa aMainPage.xaml.cs
a través también de su constructor. En el proyecto compartido (Xamarin Forms), debes asegurarte de que la ruta de la base de datos se pase correctamente al inicializar elDatabaseHelper
.
public partial class App : Application
{
public App(string dbPath)
{
InitializeComponent();
MainPage = new MainPage(dbPath);
}
}
Lógica para Guardar y Mostrar Datos
Ahora desarrollaremos la lógica en C# para manejar las acciones de la interfaz, como guardar, mostrar y eliminar personas.
Código C# para la Lógica en el Archivo MainPage.xaml.cs
:
using System;
using Xamarin.Forms;
using SQLiteApp.Models;
using SQLiteApp.Services;
namespace SQLiteApp
{
public partial class MainPage : ContentPage
{
// Instancia del controlador de base de datos (DatabaseHelper)
private readonly DatabaseHelper _databaseHelper;
// Persona seleccionada actualmente
private Person _selectedPerson;
// Constructor de la página principal, que recibe el path de la base de datos
public MainPage(string dbPath)
{
InitializeComponent();
// Inicialización del helper con la ruta de la base de datos proporcionada
_databaseHelper = new DatabaseHelper(dbPath);
// Cargar personas almacenadas al iniciar la aplicación
LoadPeople();
}
// Método para cargar personas desde la base de datos y mostrar en la lista
private async void LoadPeople()
{
// Obtener todas las personas almacenadas
var peopleList = await _databaseHelper.GetAllPeopleAsync();
peopleListView.ItemsSource = peopleList; // Mostrar la lista en la ListView
}
// Método que se ejecuta cuando se hace clic en el botón "Guardar Persona"
private async void OnSaveClicked(object sender, EventArgs e)
{
// Validación simple de los campos de entrada
if (string.IsNullOrWhiteSpace(nameEntry.Text) || string.IsNullOrWhiteSpace(ageEntry.Text))
{
await DisplayAlert("Error", "Por favor ingresa el nombre y la edad", "OK");
return;
}
// Si no hay una persona seleccionada, se creará una nueva persona
if (_selectedPerson == null)
{
var newPerson = new Person
{
Name = nameEntry.Text,
Age = int.Parse(ageEntry.Text)
};
// Insertar la nueva persona en la base de datos
await _databaseHelper.InsertPersonAsync(newPerson);
await DisplayAlert("Éxito", "Persona guardada correctamente", "OK");
}
else
{
// Si hay una persona seleccionada, actualizamos los datos
_selectedPerson.Name = nameEntry.Text;
_selectedPerson.Age = int.Parse(ageEntry.Text);
await _databaseHelper.UpdatePersonAsync(_selectedPerson);
await DisplayAlert("Éxito", "Persona actualizada correctamente", "OK");
}
// Recargar la lista de personas y limpiar el formulario
_selectedPerson = null;
nameEntry.Text = string.Empty;
ageEntry.Text = string.Empty;
deleteButton.IsEnabled = false; // Deshabilitar botón de eliminar
LoadPeople();
}
// Método que se ejecuta cuando se selecciona una persona en la lista
private void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem != null)
{
// Obtener la persona seleccionada
_selectedPerson = (Person)e.SelectedItem;
// Cargar los datos de la persona seleccionada en los campos de texto
nameEntry.Text = _selectedPerson.Name;
ageEntry.Text = _selectedPerson.Age.ToString();
// Habilitar el botón de eliminar
deleteButton.IsEnabled = true;
}
}
// Método que se ejecuta cuando se hace clic en el botón "Eliminar Persona"
private async void OnDeleteClicked(object sender, EventArgs e)
{
if (_selectedPerson != null)
{
// Eliminar la persona seleccionada de la base de datos
await _databaseHelper.DeletePersonAsync(_selectedPerson);
await DisplayAlert("Éxito", "Persona eliminada correctamente", "OK");
// Recargar la lista de personas y limpiar el formulario
_selectedPerson = null;
nameEntry.Text = string.Empty;
ageEntry.Text = string.Empty;
deleteButton.IsEnabled = false;
LoadPeople();
}
}
}
}
Nota: El constructor de la clase
MainPage
acepta un parámetrostring dbPath
, que es la ruta de la base de datos. EstedbPath
es necesario para inicializar el helper de la base de datos (DatabaseHelper
). Esto permite que la clase sea más flexible y que puedas pasarle cualquier ruta de base de datos cuando crees una instancia de la página.
Interfaz de Usuario para la Persistencia de Datos
Aquí desarrollaremos una interfaz de usuario que permita al usuario ingresar, ver, actualizar y eliminar datos de personas. Utilizaremos un ContentPage
de Xamarin Forms con XAML para definir la interfaz.
Código XAML para la Interfaz de Usuario en el Archivo MainPage.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"
x:Class="SQLiteApp.MainPage">
<ContentPage.Content>
<StackLayout Padding="10">
<!-- Entrada de Texto para el Nombre de la Persona -->
<Entry x:Name="nameEntry"
Placeholder="Nombre"
FontSize="Medium"
VerticalOptions="Center" />
<!-- Entrada de Texto para la Edad de la Persona -->
<Entry x:Name="ageEntry"
Placeholder="Edad"
Keyboard="Numeric"
FontSize="Medium"
VerticalOptions="Center" />
<!-- Botón para Agregar o Actualizar la Persona -->
<Button Text="Guardar Persona"
FontSize="Large"
Clicked="OnSaveClicked" />
<!-- Lista de Personas Almacenadas -->
<ListView x:Name="peopleListView"
VerticalOptions="FillAndExpand"
ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}"
Detail="{Binding Age}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- Botón para Eliminar la Persona Seleccionada -->
<Button Text="Eliminar Persona"
FontSize="Large"
Clicked="OnDeleteClicked"
IsEnabled="False"
x:Name="deleteButton"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
-
Entry (
nameEntry
yageEntry
): Dos campos de entrada para capturar el nombre y la edad de la persona. ElPlaceholder
define el texto de ayuda que aparece cuando el campo está vacío. -
Button (
Guardar Persona
): Botón que desencadena la acción de guardar o actualizar los datos en la base de datos. El métodoOnSaveClicked
está vinculado al eventoClicked
de este botón. -
ListView (
peopleListView
): Lista que muestra las personas almacenadas en la base de datos. Cada persona aparece con su nombre y edad. El eventoItemSelected
se dispara cuando un usuario selecciona un ítem. -
Button (
Eliminar Persona
): Botón para eliminar la persona seleccionada en la lista. Inicialmente está deshabilitado (IsEnabled="False"
) y solo se habilita cuando hay una selección en la lista.
Conclusión
Este artículo ha mostrado cómo implementar la persistencia de datos en una aplicación Xamarin Forms utilizando SQLite. Hemos configurado una base de datos, definido una clase de modelo, creado la lógica para almacenar y recuperar datos, y construido una interfaz de usuario básica. Puedes seguir expandiendo este ejemplo añadiendo funcionalidades como actualizaciones de datos, búsquedas, y mejoras en la interfaz de usuario.
Nuevo comentario
Comentarios
No hay comentarios para este Post.