Una característica que es de suma importancia en nuestras aplicaciones móviles es la capacidad de persistir la información que se genera en ella para que esté disponible para uso posterior. Por ejemplo, podemos almacenar la información de inicio de sesión del usuario, las recetas favoritas o tal vez la cantidad de PokeInciensos que tiene nuestro personaje.

Además de que, si la información de nuestra app es almacenada localmente, es posible darle al usuario una sensación de rapidez. Ya que de otro modo tendríamos que obtener los datos de un servicio externo, como un servicio web, lo cual toma tiempo dependiendod de la red a la que esté conectado el usuario.

Para persistir los datos podemos usar muchas opciones, desde archivos de texto plano, las shared preferences o NSDefaults, hasta aventurarnos a usar SQLite; todas tienen sus ventajas y sus desventajas, pero si lo que quieres es almacenar pares clave-valor, debes considerar el uso de Akavache.

Akavache, que es una pequeña librería que se puede descargar de NuGet, nos ahora mucho del trabajo necesario para realizar la persistencia de información dentro de nuestra app. Akavache está basado en SQLite3 y permite almacenar todo tipo de objetos asociados a una llave de tipo string.

El código

ApplicationName

Antes de comenzar a utilizar cualquier método relacionado con Akavache, es necesario establecer el nombre de nuestra aplicación usando la propiedad estática ApplicationName de la clase BlobCache:

BlobCache.ApplicationName = "AvakacheSample";

BlobCache

La forma de acceder al almacenamiento es através de la clase estática BlobCache y cualquiera de sus propiedades:

  • LocalMachine, almacenamiento convencional
  • Secure, almacenamiento encriptado
  • UserAccount, almacenamiento que se sincroniza a través de dispositivos de la misma familia

Para este post usaré únicamente LocalMachine o Secure.

InsertObject

Comencemos con algo simple: Supongamos que tenemos una clase que representa un usuario, con las propiedades Username y FullName, para almacenar debemos hacer uso del método InsertObject:

await BlobCache
    .LocalMachine
    .InsertObject("user_key",
                  new Usuario
                  {
                      Username = usernameEntry.Text,
                      FullName = fullNameEntry.Text
                  },
                  DateTimeOffset.Now.AddSeconds(15));

Como puedes ver, el primer argumento es una cadena que será la llave, el segundo argumento es el objeto a guardar y el tercer argumento es un DateTimeOffset opcional, nos ayuda a establecer una fecha de expiración de la información guardada, si no lo enviamos la información permanecerá "vigente" indeterminadamente. Presta atención a que en este caso, el objeto únicamente está vigente por quince segundos.

Vacumm

El método Vacumm sirve para remover del almacenamiento los valores expirados, por ejemplo, si esperamos 15 segundos después de haber ejecutado el código anterior, nuestro objeto ya no existirá:

await BlobCache.LocalMachine.Vacuum();

GetObject

Para recuperar un objeto es necesario el uso del método genérico GetObject<T>, este únicamente recibe la llave del elemento que recuperar:

try
{
    var usuario = await BlobCache
                    .LocalMachine
                    .GetObject<Usuario>("user_key");
}
catch(KeyNotFoundException ex)
{
}

Nota cómo es que esta llamada está envuelta en un bloque try-catch ya que Akavache lanza una excepción en caso de que la llave solicitada ya no exista en el almacenamiento.

SaveLogin

Como lo mencioné antes, también podemos hacer uso de un almacenamiento seguro, en donde podemos almacenar, entre otras cosas, información del inicio de sesión de los usuarios. Para esto tenemos a nuestra disposición el método SaveLogin:

await BlobCache.Secure.SaveLogin(userEntry.Text,
                                 passEntry.Text,
                                 "thatcsharpguy.com",
                                 DateTimeOffset.Now.AddDays(7));

Los argumentos son:

  1. Nombre de usuario
  2. Password
  3. Host (opcional), que podríamos ver como la llave de ese login
  4. Fecha de expiración (opcional)

GetLoginAsync

Para recuperar la información de inicio de sesión basta con llamar al método GetLoginAsync pasándole el host deseado como argumento. Este método también lanzará una excepción si la información solicitada no existe:

try
{
    var loginInfo = await BlobCache.Secure.GetLoginAsync("thatcsharpguy.com");
}
catch(KeyNotFoundException ex)
{
}

Cacheando objetos

Para los siguientes ejemplos tomaremos en cuenta el siguiente método asíncorono, que lo único que hace es esperar dos segundos antes de devolver la fecha y hora actuales:

async Task<DateTime> GetDateTime()
{
    await Task.Delay(2000);
    return DateTime.Now;
}

GetOrFetchObject

El método GetOrFetchObject es muy parecido a GetObject, con la salvedad de que este método no lanza una excepción si no existe el elemento buscado. Para no fallar, recibe como argumento un delegado Func<T> que ejecutará en caso de ser necesario. El valor devuelto por este delegado se guardará en la memoria y se devolverá al usuario:

var dt = await BlobCache.LocalMachine
                        .GetOrFetchObject("Date", () => GetDateTime());
SetValue(dt);

GetAndFetchLatest

De igual manera, supongamos que tenemos información guardada en el dispositivo, pero es probable que tengamos información más reciente en el servidor. El método GetAndFetchLatest es parecido en argumentos a GetOrFetchObject (también recibe un delegado), pero este realiza dos cosas:

  1. Devuelve inmediatamente el valor almacenado en el dispositivo (si existe)
  2. Ejecuta el delegado para recuperar el valor más reciente

Para llamar a este método NO debemos utilizar la palabra reservada await, sino, usar el método Suscribe que en pocas palabras indica una acción que se debe realizar cada que se obtenga un valor como resultado de la ejecución del método (en nuestro caso se ejecutará dos veces, una con el valor existente y otra cuando se obtenga el nuevo valor):

BlobCache.LocalMachine
            .GetAndFetchLatest(Key,
                            () => GetDateTime())
            .Subscribe((obj) => 
{
    Device.BeginInvokeOnMainThread(() =>
    {
        SetValue(obj);
    });
});

No hay nada mejor que ver en funcionamiento los métodos mencionados, presta atención al reloj sobre el simulador y a cómo es que se comporta cada botón:

Conclusión

Aún hay algunas cuantas funciones de Akavache de las que no hablé en este post, te invito a consultar la documentación y este otro post en el que hay mucha información. No está de más decir que Akavache es muy potente, pero que usada junto con otras librerías sus capacidades se multiplican. En general su experiencia de uso es bastante buena, salvo por algunos pequeños requerimientos en las plataformas de Windows, pero nada del otro mundo.

Instalación

Como siempre, hay que buscar en el gestor de paquetes de NuGet: Akavache

O desde la consola:

PM> Install-Package Akavache

Recuerda que si lo usas en Xamarin.Forms debes instalar Akavache en todos tus proyectos (núcleo y clientes) para que funcione correctamente. No olvides revisar el código fuente de Akavache en GitHub (thanks Mr. Paul Betts).

En cuanto a este post, también te invito a descargar el código de la aplicación que ves en el vídeo anterior, está en este repositorio de GitHub.