[Xamarin.Forms] Introducción al uso de efectos

Introducción

Xamarin.Forms añade una capa de abstracción sobre la capa de la interfaz de usuario permitiendo definir la misma una única vez siendo válida para todas las plataformas.

Xamarin.Forms
Xamarin.Forms

Cuenta con páginas, layouts y controles que son renderizados de forma diferente en cada plataforma. Se utiliza una clase Renderer distinta en cada plataforma y encargada de crear un control nativo y añadirlo en pantalla.

¿Qué es un efecto?

Un efecto permite el acceso al control nativo de cada plataforma con el objetivo de personalizarlo, principalmente aplicando pequeños cambios estéticos o de comportamiento. Permiten simplificar la personalización del control y sobretodo se convierten en «piezas» reutilizables de código incluso aceptando parametrización.

¿Por qué usar efectos y no un Custom Renderer?

Tener acceso a un control nativo, poder personalizarlo y además tenerlo todo en una pieza reutilizable está al alcance de nuestra mano gracias al uso de efectos.

¿Cuándo utilizamos entonces un Custom Renderer, y cuándo un efecto?

  • Si necesitamos crear un nuevo control o  reemplazar el control específico implementado por Xamarin.Forms de una plataforma, necesitamos un Custom Renderer.
  • Si sólo necesitamos modificar o añadir alguna propiedad que modifica la apariencia o comportamiento del control nativo implementado por Xamarin.Forms, efecto.

Crear un efecto

El proceso de creación de un efecto, se puede resumir en una serie de sencillos pasos:

  1. Crear en la PCL una clase que herede de RoutingEffect. Código independiente de la plataforma encargado de hacer el wrapping del efecto. Podemos definir distintas propiedades que permitan modificar la acción realizada por el efecto. Por ejemplo, en un efecto encargado de aplicar Blur a una imagen, se puede definir una propiedad encarga de aplicar mayor o menor distorsión.
  2. Crear clases en cada plataforma soportada que hereden de PlatformEffect.
  3. Sobrecargar el método OnAttached y añadir la lógica de personalización del control.
  4. Sobrecargar el método OnDetached y añadir lógica de liberación de recursos.
  5. Añadir el atributo ResolutionGroupName. Este atributo permite establecer el nombre del creador o compañia tras el efecto. Recuerda que uno de los objetivos fundamentales de los efectos es lograr permitir compartir y reutilizar con suma facilidad. Con este atributo se previenen colisiones con otros efectos que compartan nombre.
  6. Añadir el atributo ExportEffect. Este atributo registra el efecto con un identificador único usado por Xamarin.Forms, junto al nombre del grupo, permite localizar y aplicar el efecto.

Para comprender el proceso de creación de un efecto vamos a crear uno paso a paso. Nuestro efecto será simple, permitirá aplicar el efecto blur a una imagen.

Comenzamos en la PCL. Dentro de la carpeta Effects, creamos una clase que herede de RoutingEffect:

public class BlurredEffect : RoutingEffect
{
     public string Url { get; set; }
     public int Radius { get; set; }

     public BlurredEffect() : base("Xamarin.BlurredImageEffect")
     {

     } 
}

Además de la implementación del constructor, donde indicamos el nombre del grupo y del efecto que utilizaremos en la implementación de cada plataforma, añadimos dos propiedades:

  • Url: La Url de la imagen a la que aplicaremos el efecto.
  • Radius: Un entero que permite establecer el nivel de distorsión aplicado. A valores más altos, mayor nivel de distorsión.

Tras la creación de la «definición» de lo que será nuestro servicio, pasamos al código específico por plataforma. Se deben de crear clases que hereden de PlatformEffect:

public class BlurredImageEffect : PlatformEffect  
{

}

PlatformEffect provocará la necesidad de implementar los métodos OnAttached y OnDetached:

protected override void OnAttached() 
{

}

protected override void OnDetached()    
{
        
}

Vamos a comprender el objetivo de cada método:

  • OnAttached: Lanzado cuando un efecto se adjunta un control Xamarin.Forms. La sobrecarga de este método en cada plataforma es el lugar idóneo para añadir código específico de plataforma que afecte al control ya sea en su apariencia o en su comportamiento.
  • OnDetached: Este método se lanza cuando el efecto se quita del control Xamarin.Forms. La sobrecarga permite añadir código específico de liberación de recursos y limpieza.

Aunque no lo usamos en nuestro efecto de blur, PlatformEffect expone el método OnElementPropertyChanged. Este método se lanza cuando una propiedad del efecto cambia. Lugar idóneo para responder a cambios de la propiedades.

Implementamos el código del método OnAttached en Android:

protected override void OnAttached()
{
     try
     {
          var imageView = Control as ImageView;
          var effect = (BlurredEffect)Element.Effects.FirstOrDefault(e => e is BlurredEffect);
          if (effect != null)
          {
               var d = imageView.Drawable;
               if (d != null)
               {
                    var bitmap = GetImageBitmapFromUrl(effect.Url);

                    if (bitmap != null)
                    {
                        imageView.SetImageBitmap(CreateBlurredImage(effect.Radius, bitmap));
                        imageView.Invalidate();
                    }
                }
          }
     }   
     catch (Exception ex)
     {
          Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
     }
}

Cada clase específica de plataforma que hereda de PlatformEffect cuenta con:

  • Control: Control nativo usado por Xamarin.Forms como implementación del control Xamarin.Forms. Por ejemplo, en Android en el caso de una Image, hablaríamos de un ImageView.
  • Element: Control Xamarin.Forms renderizado.

Analicemos el código anterior. Comienza accediendo al efecto, disponible en la colección Effects del control representado por Element.

Tras acceder al efecto, se crea un Bitmap partiendo de la Url recibida como parámetro en la propiedad Url, y se aplica el efecto Blur utilizando la imagen anterior y aplicando el grado de distorsión utilizando la propiedad Radius.

El método encargado de crear el Bitmap en base a la Url:

prprivate Bitmap GetImageBitmapFromUrl(string url)   
{            
     Bitmap imageBitmap = null;
          
     using (var webClient = new WebClient())
     {
          var imageBytes = webClient.DownloadData(url);
          if (imageBytes != null && imageBytes.Length > 0)
          {
               imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
          }
     }

     return imageBitmap;
}

Utiliza un WebClient para descargar la información de la imagen y BitmapFactory para crear el Bitmap usando los datos descargados.

El método encargado de aplicar el efecto Blur:

private Bitmap CreateBlurredImage(int radius, Bitmap originalBitmap)
{
     Bitmap blurredBitmap;
     blurredBitmap = Bitmap.CreateBitmap(originalBitmap);

     var rs = RenderScript.Create(Forms.Context);
     var input = Allocation.CreateFromBitmap(rs, originalBitmap, Allocation.MipmapControl.MipmapFull, AllocationUsage.Script);
     var output = Allocation.CreateTyped(rs, input.Type);

     var script = ScriptIntrinsicBlur.Create(rs, Android.Renderscripts.Element.U8_4(rs));
     script.SetInput(input);
     script.SetRadius(radius);
     script.ForEach(output);

     output.CopyTo(blurredBitmap);

     return blurredBitmap;
}

Basado en RenderScript para aplicar el efecto.

Nos faltaría aplicar las etiquetas que permitan establecer el nombre del grupo y la exportación del efecto:

[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(BlurredImageEffect), "BlurredImageEffect")]

De igual forma, en otras plataformas (iOS y Windows) se debe de crear la clase e implementar PlatformEffect.

Utilizar un efecto

Desde la PCL, en la vista compartida definida en XAML, para utilizar el efecto se debe utilizar el espacio de nombre donde se ha definido:

xmlns:effects="clr-namespace:BlurImageEffect.Effects"

Y utilizarlo:

<Label 
     Text="Image"/>
<Image
     Source="https://www.xamarin.com/content/images/pages/branding/assets/xamarin-logo.png"/>
<Label 
      Text="Blurred Image"/>
<Image>
     <Image.Effects>
        <effects:BlurredEffect
             Url="https://www.xamarin.com/content/images/pages/branding/assets/xamarin-logo.png"
             Radius="20"/>
     </Image.Effects>
</Image>

El efecto BlurredEfect se adjunta a la segunda imagen añadiéndolo a la colección Effects. Xamarin.Forms utilizará Effect.Resolve para devolver el efecto especificado utilizando una concatenación del nombre de grupo (ResolutionGroupName) junto a su identificador (especificado con el atributo ExportEffect).

El resultado:

BlurredImageEffect
BlurredImageEffect

Tenéis el código fuente del ejemplo utilizado disponible en GitHub:

Ver GitHub

Recuerda, cualquier tipo de duda o sugerencia es bienvenida en los comentario del artículo.

Más información

[Evento SVQXDG] Xamarin Dev Days en Sevilla!

XamarinEl evento

Xamarin Dev Days son una serie de eventos celebrados a lo largo de todo el mundo que ofrecen la posibilidad de conocer las últimas novedades relacionadas con el desarrollo Xamarin, poder disfrutar de talleres y por supuesto, compartir momentos de networking.

Xamarin Dev Days
Xamarin Dev Days

La agenda:

• 9:00AM Registro y bienvenida.

• 9:30 – 10:10AM Introducción a Xamarin. Introducción a los conceptos básicos tanto de Xamarin Classic como de Xamarin.Forms así como a la integración con Visual Studio, Xamarin Test Cloud, etc. Por Josué Yeray.

• 10:20 – 11:00AM Xamarin Forms. Tras una introducción a conceptos básicos se verá como estructurar el proyecto y una serie de buenas prácticas para compartir la mayor cantidad de código posible. Finalmente se terminará con las últimas novedades como Xamarin.Forms Previewer, DataPages, Themes o Native Embedding. Por Javier Suárez.

• 11:10 – 11:50AM Xamarin + Azure. Gracias al Azure podemos crear servicios de backend, notificaciones push y otras características esenciales de forma compartida. En esta sesión aprenderemos como sacarle partido a Azure desde nuestras aplicaciones Xamarin. Por Marcos Cobeña.

• 12:00 – 2:00PM Hands on Lab. ¿Crear una aplicación Xamarin paso a paso con MVVM, uso de servicios web y accediendo a características de cada plataforma?. En este taller realizaremos una aplicación con esas características entre todos. Anímate y ven con tu portátil preparado!

• 2:00 – 3:00PM Comida.

• 3:00 – 4:00PM Hands on Lab.

El lugar

El evento se celebrará en el WorkINCompany. Dirección detallada:

Calle Rioja 13, 1º C, Sevilla

WorkINCompany
WorkINCompany

La fecha

El evento tendrá lugar el próximo Sábado, 10 de Septiembre de 9:00h a 16:00h. Tendremos tres sesiones técnicas de  40 minutos de duración cada una junto a un taller de 3 horas de duración. Además contaremos con regalos y sorpresas.

¿Te apuntas?

Más información

[Material XDev Madrid] WinObjc: De iOS a Windows y extendiendo Xamarin.Forms

El evento

El pasado 09 de Agosto, desde el grupo XDev en Madrid, se organizaba un evento veraniego centrado en desarrollo móvil multiplataforma, con temas como convertir aplicaciones de iOS a UWP o extender Xamarin.Forms, además de tener momentos para el networking y algun que otro detalle con los asistentes.

El material

Pude participar en el evento con dos sesiones.

En la primera de ellos, nos centramos en el Windows Bridge para iOS, WinObjC. Convertimos algunas aplicaciones iOS a UWP e incluso vimos como añadir características específicas de la plataforma Windows como el uso de Live Tiles.

En la segunda sesión, nos centramos en como extender Xamarin.Forms para poder acceder a características específicas de cada plataforma, crear nuevos controles o efectos con el objetivo de ofrecer la mejor experiencia posible en cada plataforma, aun compartiendo gran parte de la interfaz de usuario.

En cuanto a las demos técnicas realizadas, las tenéis disponible en GitHub:

Ver GitHub

Quisiera terminar añadiendo algunos agradecimientos. Gracias a todos los asistentes por asistir además de la constante participación con preguntas, en estas fechas y con tan poca antelación en el anuncio del evento es destacable; gracias a Dachi Gogotchuri por toda la ayuda en la organización y gracias a Liferay por sus increíbles instalaciones cediéndonos una sala. Nos vemos en la próxima!

Más información

[Xamarin] Xamarin UITest y Test Recorder desde Visual Studio

Bug - 02La calidad en movilidad cuesta

La calidad en el software es algo innegociable. Un buen proceso en el desarrollo y gestión del proceso es fundamental para conseguir ese objetivo. Debemos entregar Apps móviles perfectamente adaptadas a cada plataforma, ofreciendo la mejor experiencia de usuario posible pero sobretodo, funcional. Una App funcional debe cubrir y cumplir unos mínimos exigentes de calidad.

Como desarrolladores, somo humanos y el código no estara libre de errores. Sin embargo, el proceso que apliquemos para la detección y corrección a los mismos, es vital.

Podemos crear dos tipos de pruebas diferenciadas:

  • Pruebas unitarias: Pruebas de pequeñas unidades funcionales de nuestra App. Utilizaremos NUnit para realizar estas pruebas unitarias generalmente de ViewModels, Helpers y Servicios.
  • Pruebas de interfaz de usuario: Pruebas sobre la interfaz de usuario, escritura en cajas de texto, pulsaciones de botones, etc. Utilizaremos Xamarin UITest para estas pruebas.

Xamarin UITest

Xamarin.UITest es un framework de testing que permite realizar pruebas de comportamiento de la aplicación automatizando interacciones con la misma.

Integrado con Xamarin.iOS y Xamarin.Android, aunque puede usarse con proyectos iOS y Android escritos con Objective-C y Java, permite automatizar la gestión con la interfaz con pulsación de botones, introducir textos, gestos, tomar capturas, etc.

Cada UITest es un método escrito en C# que sigue el patrón Arrange-Act-Assert:

  • Arrange: El test inicializa todo lo necesario para que pueda ser lanzado.
  • Act: La interacción automatizada con la aplicación, introducir textos, pulsar botones, etc.
  • Assert: El test verifica resultados de las acciones realizadas en Act. Por ejemplo, verifica que tras introducir un valor incorrecto en una caja de texto, aparece un mensaje de error.

Nuestra aplicación:

Calculadora
Calculadora

Para poder analizar y profundizar en las pruebas, vamos a utilizar una aplicación realmente sencilla, una calculadora. Vamos a ver y analizar el proyecto de tipo Xamarin UITest de nuestra aplicación.

Todas las interacciones automatizadas con la aplicación ocurren mediante una instancia de Xamarin.UITest.IApp. Esta interfaz define los métodos utilizados para realizar la interacción. Cuenta con dos implementaciones:

  • Xamarin.UITest.iOS.iOSApp: Automatiza en aplicaciones iOS.
  • Xamarin.UITest.Android.AndroidApp: Automatiza en aplicaciones Android.

Ambos objetos, iOSApp y AndroidApp, se instancian utilizando la clase ConfigureApp. Esta clase se asegura de instanciar correctamente la aplicación en cada caso.

Tras instanciar la aplicación, la interacción con la aplicación se realiza utilizando querys. Los métodos de Xamarin.UITest esperan en la mayoría de casos un parámetro de tipo Func<AppQuery, AppQuery> para localizar los elementos visuales.

En nuestra aplicación, queremos realizar una prueba sencilla de suma, donde accederemos al botón numérico dos, al operador de suma y al símbolo igual. Esto nos permitirá verificar que el comportamiento de sumar dos más dos es el esperado.

Utilizando querys accedemos a cada elemento visual:

static readonly Func<AppQuery, AppQuery> TwoButton = c => c.Marked("Digit2");
static readonly Func<AppQuery, AppQuery> PlusButton = c => c.Marked("Operator+");
static readonly Func<AppQuery, AppQuery> EqualsButton = c => c.Marked("OperatorEquals");

Por supuesto, antes de lanzar el test, debemos realizar el proceso de inicialización de IApp. Normalmente, en una clase de tests tendremos agrupados múltiples tests. Cada uno de ellos debe correr en una condiciones limpias, es decir, se debe realizar la inicialización de IApp en cada caso. Este proceso se suele realizar en el método SetUp.

private IApp _app;

[SetUp]
public void SetUp()
{
     switch (TestEnvironment.Platform)
     {
          case TestPlatform.Local:
               var appFile =
                    new DirectoryInfo(Path.Combine("..", "..", "testapps"))
                        .GetFileSystemInfos()
                        .OrderByDescending(file => file.LastWriteTimeUtc)
                        .First(file => file.Name.EndsWith(".app") || file.Name.EndsWith(".apk"));

                _app = appFile.Name.EndsWith(".app")
                        ? ConfigureApp.iOS.AppBundle(appFile.FullName).StartApp() as IApp
                        : ConfigureApp.Android.ApkFile(appFile.FullName).StartApp();
                break;
           case TestPlatform.TestCloudiOS:
                _app = ConfigureApp.iOS.StartApp();
                break;
           case TestPlatform.TestCloudAndroid:
                _app = ConfigureApp.Android.StartApp();
                break;
     }
}

Por último, tras tener la inicialización (creada automáticamente al crear nuevo proyecto de tipo Xamarin UITest) y las querys, nuestro test de suma de dos más dos será:

[Test]
public void TheTwoPlusTwoIsFourTest()
{
     _app.WaitForElement(c => c.Marked("OperatorEquals"));

     _app.Tap(TwoButton);
     _app.Tap(PlusButton);
     _app.Tap(TwoButton);
     _app.Tap(EqualsButton);
     _app.Screenshot("When I get the result value");

     AppResult[] results = _app.WaitForElement(c => c.Marked("DisplayValue").Text("4"));

     Assert.IsTrue(results.Any());
}

Pulsamos en cada botón, tomamos captura del resultado y finalmente verificamos el resultado.

Corriendo Xamarin UITests desde Visual Studio

Para lanzar pruebas unitarias y Xamarin.UITests desde Visual Studio, utilizaremos el explorador de pruebas disponible desde el menu Test.

Explorador de pruebas
Explorador de pruebas

En nuestro proyecto con la calculadora realizada en Xamarin.Forms con pruebas con Xamarin.UITests vemos lo siguiente…

¿Por qué no aparecen los tests?
¿Por qué no aparecen los tests?

No aparecen, ¿qué ocurre?. El explorador de pruebas solo muestra por defecto tests realizados con MSTests. En nuestro proyecto Xamarin utilizamos NUnit para las pruebas unitarias y además XAmarin UITests, hace uso de NUnit también.

Vamos a solucionar el problema añadiendo la posibilidad de lanzar tests realizados con NUnit. Desde el menu de Herramientas->Extensiones y actualizaciones…

Extensions
Extensiones y actualizaciones

Dentro del conjunto Online buscamos por NUnit Test Adapter. Debemos instalar el adaptador correspondiente a NUnit 2 a pesar de estar disponible la versión correspondiente a NUnit 3. Esto es así ya que Xamarin UITest hace uso de la versión 2 de NUnit.

NUnit Test Adapter
NUnit Test Adapter

Tras instalar la extensión, al compilar el proyecto:

Aparecen los tests!
Aparecen los tests!

Ahora podemos lanzar o depurar cualquiera de las pruebas (o todas) sencillamente haciendo clic derecho sobre las mismas.

Xamarin Test Recorder

Podemos crear Xamarin UITests a mano y haciendo uso del REPL, sin embargo, es mucho más sencillo hacer uso de Xamarin Test Recorder.

Xamarin Test Recorder es una herramienta que nos permite, seleccionar un paquete de aplicación, lanzar la aplicación, interaccionar con la interfaz de usuario capturando los movimientos necesarios y la herramienta generará automáticamente el test en base a la interacción realizada.

Para poder utilizar la herramienta desde Visual Studio debemos instalar la extensión Xamarin Test Recorder 2015.

NOTA: Existe una versión de la extensión preparada para usar desde Visual Studio 2013.

Xamarin Test Recorder Extension
Xamarin Test Recorder Extension

Tras instalar la extensión, para poder utilizarla debemos cumplir unos requisitos básicos:

  1. Tener abierto un proyecto de tipo Xamarin UITest. En caso de no contar con ninguno en el proyecto, crear uno.
  2. Tener abierto un emulador o bien conectado por USB un dispositivo.

Tras cumplir los requisitos, podemos ver el siguiente indicador visual:

Indicador visual
Indicador visual

Pulsando sobre el indicador visual, nos aparecerá una opción para grabar un nuevo test.

Grabar nuevo test
Grabar nuevo test

En este punto, podremos elegir el paquete de la aplicación que deseamos probar.

Seleccionar paquete
Seleccionar paquete

Tras seleccionar el paquete de la aplicación, se lanzará la misma en el emulador o dispositivo conectado además de crear un nuevo método de Test vacío en el editor.

Con la aplicación en ejecución, y la grabación del test, cualquier interacción realizada quedará reflejada en un nuevo paso del nuevo método de test creado por la herramienta.

Una vez completada la interacción, vamos a detener la grabación. Pulsamos sobre el icono de grabación y nos aparecerá un menu como el siguiente:

Detener grabación
Detener grabación

Bastará con elegir la primera de las opciones para detener la grabación. Tendremos el código C# resultante de las interacciones realizadas en un nuevo método creado por la herramienta.

En el proceso de grabación del test, podemos tomar capturas de pantalla en cualquier momento pulsando sobre el icono de grabación y utilizando la opción Take Screenshot.

Sencillo, ¿cierto?. Las pruebas creadas las podemos lanzar directamente en local en nuestros emuladores o dispositivos físicos o hacer uso de Xamarin Test Cloud.

Tenéis el código fuente del ejemplo utilizado disponible en GitHub:

Ver GitHub

Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

Más información

[Xamarin.Forms] Utilizando DataPages

Forms-01-WFIntroducción a DataPages

En no pocas ocasiones nos planteamos realizar una aplicación sencilla para consultar información existente desde un servidor o infraestructura cloud.

En el marco del pasado Evolve 2016, Xamarin anunciaba la disponibilidad e la preview de Xamarin.Forms DataPages.

Los DataPages llegan para facilitar la creación de aplicaciones de tipo maestro-detalle con acceso a una fuente de información de la forma más rápida y sencilla posible. Se renderizarán listados y detalles automáticamente en base a los datos pudiendo personalizar con el uso de diferentes controles y el uso de temas.

DataPages se puede utilizar consumiendo el paquete NuGet Xamarin.Forms.Pages.

Permite acceder a diferentes fuentes de información con data sources prefabricados:

  • JsonDataSource: Una URL que contiene un JSON.
  • AzureDataSource (paquete NuGet separado): Azure Mobile Services.
  • AzureEasyTableDataSource (paquete NuGet separado).

Para mostrar la información correspondiente a la fuente de información contamos con una serie de páginas y controles.

Las páginas disponibles son:

  • ListDataPage: Muestra un listado de elementos.
  • DirectoryPage: Listado de elementos agrupados.
  • PersonDetailPage: Página de detalles que muestra la información correspondiente a un tipo de objeto.

En cuanto a controles:

  • ListItem: Vista prefabricada destinado a mostrar un elemento de un listado. Puede mostrar título, detalles e imagen.
ListItem
ListItem
  • CardView: Este control es similar al CardView nativo de Android. Permite mostrar un texto principal, uno secundario y una imagen.
CardView
CardView
  • HeroImage: Control que permite mostrar título, detalles e imagen, dándole peso a esta última. Tambien cuenta con una propiedad para modificar el aspecto.
HeroImage
HeroImage

Preparando la solución

Comenzamos creando una aplicación Xamarin.Forms utilizando una librería portable (PCL):

Nueva aplicación Xamarin.Forms
Añadir paquetes utilizando NuGet

Nueva aplicación Xamarin.Forms

Tras crear el proyecto debemos añadir la referencia a DataPages y Themes. Para ello, utilizaremos NuGet para añadir:

  • Xamarin.Forms.Pages
  • Xamarin.Forms.Theme.Base
  • Xamarin.Forms.Themes.Light o Xamarin.Forms.Themes.Dark
Añadir paquetes utilizando NuGet
Añadir paquetes utilizando NuGet

NOTA: Debemos añadir los paquetes NuGet tanto en la libería portable como en el proyecto específico de cada plataforma, tanto en iOS como en Android.

DataPages requiere el uso de temas para poder renderizar correctamente. Para ello, en App.xaml debemos añadir el namespace detema y añadirlo en los recursos de la aplicación.

Dependiendo del tema elegido, los espacios de nombre a utilizar son:

xmlns:light="clr-namespace:Xamarin.Forms.Themes;assembly=Xamarin.Forms.Theme.Light"    
xmlns:dark="clr-namespace:Xamarin.Forms.Themes;assembly=Xamarin.Forms.Theme.Dark"

Y para añadir el tema a utilizar dentro del conjunto de recursos de la aplicación:

<ResourceDictionary MergedWith="dark:DarkThemeResources" />        
<ResourceDictionary MergedWith="light:LightThemeResources" />

Por ahora, para completar el proceso de uso del tema, debemos añadir algo de código específico por cada plataforma para cargar las librerías correspondientes.

var x = typeof(Xamarin.Forms.Themes.DarkThemeResources);    
x = typeof(Xamarin.Forms.Themes.LightThemeResources);    
x = typeof(Xamarin.Forms.Themes.Android.UnderlineEffect);

En el caso de iOS código a ejecutar en el AppDelegate, en el de Android, en la actividad principal MainActivity.

Ejemplo básico

Añadimos la vista XAML principal de la aplicación. Incluimos el espacio de nombres correspondiente a DataPages para acceder y utilizar las páginas, controles y data sources incluidos.

xmlns:p="clr-namespace:Xamarin.Forms.Pages;assembly=Xamarin.Forms.Pages"

Añadimos:

<?xml version="1.0" encoding="UTF-8"?>
<p:ListDataPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:p="clr-namespace:Xamarin.Forms.Pages;assembly=Xamarin.Forms.Pages"
             x:Class="DataPagesDemo.SessionDataPage"
             Title="Sessions" StyleClass="Events">
    <p:ListDataPage.DataSource>
        <p:JsonDataSource Source="http://demo3143189.mockable.io/sessions" />
    </p:ListDataPage.DataSource>
</p:ListDataPage>

El resultado:

El resultado
El resultado

Increíble, ¿cierto?. Vamos a analizar el código  (escaso) añadido.

Hemos modificado el tipo de página de una ContentPage, página básica para añadir contenido en Xamarin.Forms por una ListDataPage. Xamarin.Forms DataPages nos ofrece este tipo de página que nos permite crear un maestro-detalle con suma facilidad en base una fuente de información.

Utilizando la propiedad DataSource de la página, creamos un JsonDataSource que se encargará de acceder a la información realizando la petición HTTP necesaria y parseando la información para permitir mostrarla en la UI realizando bindings.

¿Dónde realizamos el enlace de la UI con la información?. La propiedad StyleClass de la página permite acceder a un estilo. En este caso se utiliza «Events», estilo prefabricado que ya define lo necesario para enlazar correctamente con la fuente de información («title», «image», «presenter»).

Tenéis el código fuente del ejemplo básico disponible en GitHub:

Ver GitHub

Añadiendo personalización

Vamos a crear un segundo ejemplo donde personalizar aspectos como la fuente de información, el aspecto visual modificando controles utilizados y por supuesto, también los enlaces a la información.

Vamos a generar una fuente de información de pruebas utilizando Json generator. Reemplazamos la fuente utilizando el datasource de tipo JsonDataSource:

<p:ListDataPage.DataSource>
     <p:JsonDataSource Source="http://www.json-generator.com/api/json/get/clOPFoTImW?indent=2" />
</p:ListDataPage.DataSource>

A continuación, vamos a modificar la propiedad DefaultItemTemplate que permite especificar la apariencia de cada elemento dentro del listado. En lugar de utilizar el control CardView utilizado por defecto en el ejemplo anterior, vamos a utilizar un ListItem.

<p:ListDataPage.DefaultItemTemplate>
     <DataTemplate>
          <ViewCell>
               <p:ListItemControl
                    Title="{p:DataSourceBinding name}" 
                    Detail="{p:DataSourceBinding email}" 
                    ImageSource="{p:DataSourceBinding picture}"
                      DataSource="{Binding Value}">
               </p:ListItemControl>
          </ViewCell>
     </DataTemplate>
</p:ListDataPage.DefaultItemTemplate>

ListItem cuenta con propiedades Title, Detail e ImageSource para especificar sus valores, utilizando la palabra reservada DataSourceBinding enlazaremos con los campos deseados del archivo JSON.

Nuestra página quedaría finalmente:

<p:ListDataPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:p="clr-namespace:Xamarin.Forms.Pages;assembly=Xamarin.Forms.Pages"
             x:Class="DataPages.MainView"
                   StyleClass="Events" 
                   Title="Users">   
    <p:ListDataPage.DataSource>
        <p:JsonDataSource Source="http://www.json-generator.com/api/json/get/clOPFoTImW?indent=2" />
    </p:ListDataPage.DataSource>
    <p:ListDataPage.DefaultItemTemplate>
        <DataTemplate>
            <ViewCell>
            <p:ListItemControl
                Title="{p:DataSourceBinding name}" 
                Detail="{p:DataSourceBinding email}" 
                ImageSource="{p:DataSourceBinding picture}"
                  DataSource="{Binding Value}">
                </p:ListItemControl>
            </ViewCell>
        </DataTemplate>
    </p:ListDataPage.DefaultItemTemplate>
</p:ListDataPage>

Tras ejecutar:

Personalización
Personalización

Además de contar con el listado, tenemos también disponible la vista de detalles mostrando más información a partir de los diferentes datos del JSON.

Y hasta aquí el repaso a las posibilidades de Xamarin.Forms DataPages. Una forma bastante rápida y sencilla para crear aplicaciones sencillas o prototipos con posibilidades interesantes en personalización y extensión. Siempre repitiendo cuatro sencillos pasos:

  1. Instalar paquetes NuGet.
  2. Seleccionar tema.
  3. Añadir DataPage.
  4. Configurar data source y personalización.

Tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

¿Y próximamente?

DataPages continuará evolucionando y madurando con varios objetivos en mente. Xamarin ya ha confirmado que:

  • En próximas versiones no necesitaremos añadir código específico de plataforma para realizar la carga de las librerías correspondientes a temas.
  • Se incluirá la posibilidad de CRUD (create-read-update-delete).

Más información

[Xamarin] Comparativa de SQLite y Realm

Sword-01-WFIntroducción

Tras la llegada de Realm a Xamarin con la promesa de una opción de base de datos sencilla y sobretodo con mejor rendimiento que el resto de opciones (incluida SQLite, la opción más usada y extendida), la duda es obvia…¿cuál usar?. En este artículo vamos a realizar unas pruebas básicas con las operaciones de CRUD para medir en tiempos el rendimiento exacto de cada opción. Además verificaremos otros aspectos como la facilidad de uso, el nivel de documentación y otros aspectos.

¿Os apuntáis?

Realm

Realm es una base de datos gratuita pensada para aplicaciones móviles, tabletas o wearables siendo una alternativa interesante a SQLite. Llega con el gran objetivo en mente de conseguir un alto rendimiento manteniendo una alta facilidad de uso.

SQLite

SQLite es un motor de base de datos Open Source utilizado en todas las plataformas móviles y adoptado tanto por Apple como Google como Microsoft. El uso de SQLite en aplicaciones móviles es una gran opción ya que:

  • La base de datos es pequeña y fácil de portar.
  • La base de datos se concentra en un pequeño archivo.
  • Implementa la mayor parte del estándar SQL92.

La aplicación para realizar pruebas

En nuestra aplicación necesitamos una única vista donde contaremos con botones para realizar las acciones básicas del CRUD junto a un área donde mostrar los resultados.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SqliteVsRealm.Views.MainView"
             Title="SQLite VS Realm">
  <ContentPage.Content>
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition Height="20"/>
        <RowDefinition Height="50"/>
        <RowDefinition Height="50"/>
        <RowDefinition Height="50"/>
        <RowDefinition Height="20"/>
        <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <Button Grid.Row="1"
              Text="Insert 1000 Items"
              Command="{Binding InsertCommand}"/>
      <Button Grid.Row="2"
              Text="Query 1000 Items"
              Command="{Binding QueryCommand}"/>
      <Button Grid.Row="3"
              Text="Delete 1000 Items"
              Command="{Binding DeleteCommand}"/>
      <Editor Grid.Row="5"
              Text="{Binding Log}"
              FontSize="10"/>
    </Grid>
  </ContentPage.Content>
</ContentPage>

El resultado visual:

Nuestra aplicación para realizar pruebas
Nuestra aplicación para realizar pruebas

Enlazamos la View con la ViewModel estableciendo una instancia de la ViewModel a la propiedad BindingContext de la página.

BindingContext = App.Locator.MainViewModel;

En cuanto a cada botón, cada uno de ellos estará enlazado a un comando:

private ICommand _insertCommand;
private ICommand _queryCommand;
private ICommand _deleteCommand;
 
public ICommand InsertCommand
{
     get { return _insertCommand = _insertCommand ?? new DelegateCommand(InsertCommandExecute); }
}
 
public ICommand QueryCommand
{
     get { return _queryCommand = _queryCommand ?? new DelegateCommand(QueryCommandExecute); }
}
 
public ICommand DeleteCommand
{
     get { return _deleteCommand = _deleteCommand ?? new DelegateCommand(DeleteCommandExecute); }
}
 
private async void InsertCommandExecute()
{
 
}
 
private async void QueryCommandExecute()
{
 
}
 
private async void DeleteCommandExecute()
{
 
}

Sencillo, ¿cierto?.

La comparativa

A continuación, vamos a utilizar la aplicación creada para medir rendimiento de ambas opciones además de valorar otros aspectos de peso a la hora de determinar el uso de una u otra opción. Vamos a valorar aspectos como:

  • Facilidad de uso
  • Rendimiento
  • Documentación
  • Mantenimiento

Facilidad de uso

En el arranque de uso de SQLite y Realm tenemos que utilizar paquetes NuGet. En el caso de Realm todo viene empaquetado en un único paquete mientras que en el caso de SQLite utilizaremos varios paquetes (SQLite.Net-PCL, SQLite.Net.Core-PCL y SQLite.Net.Async-PCL).

Tras añadir los paquetes debemos realizar la configuración básica. En el caso de Realm es realmente reducida mientras que en SQLite si que necesitamos un poco de trabajo para especificar la ruta a la base de datos en cada plataforma para poder establecer la conexión.

public interface ISQLite
{
     SQLiteAsyncConnection GetConnection();
}

La implementación de ISQLite nos permite establecer la conexión con la base de datos.

[assembly: Dependency(typeof(SQLiteClient))]
namespace TodoSqlite.Droid.Services
{
    public class SQLiteClient : ISQLite
    {
        public SQLiteAsyncConnection GetConnection()
        {
            var sqliteFilename = "Todo.db3";
            var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
 
            var path = Path.Combine(documentsPath, sqliteFilename);
 
            var platform = new SQLitePlatformAndroid();
 
            var connectionWithLock = new SQLiteConnectionWithLock(
                                         platform,
                                         new SQLiteConnectionString(path, true));
 
            var connection = new SQLiteAsyncConnection(() => connectionWithLock);
 
            return connection;
        }
    }
}

Por último, el trabajo con cada base de datos es realmente similar. Tenemos métodos para cada acción básica, CRUD y tareas de gestión y mantenimiento directamente en C#. Veamos como obtener datos de una tabla.

En Realm:

public IList<TodoItem> GetAll()
{
     var result = _realm.All<TodoItem>().ToList();
 
     return result;
}

En SQLite:

public async Task<IList<TodoItem>> GetAll()
{
     var items = new List<TodoItem>();
     using (await Mutex.LockAsync().ConfigureAwait(false))
     {
          items = await _sqlCon.Table<TodoItem>().ToListAsync().ConfigureAwait(false);
     }
 
     return items;
}

El número de líneas y opciones en este caso estan realmente parejas. Sin embargo, la preparación inicial es practicamente nula en el caso de Realm.

Ganador: Realm

Rendimiento

Llegamos a uno de los puntos fuertes o generalmente de mayor preocupación. Desde Realm se habla del rendimiento como una de sus principales bazas, pero…¿es realmente más rápido?, ¿cuánto más?.

A continuación, vamos a centrarnos en la lógica de cada botón de nuestra interfaz. A la hora de insertar datos:

[sourcecode language=»vb»]
private async void InsertCommandExecute()
{
     var watch = System.Diagnostics.Stopwatch.StartNew();
     for (int i = 0; i < 1000; i++)
     {
          await _sqliteService.Insert(new SqliteTodoItem
          {
               Id = i + 1,
               Name = string.Format("Name {0}", i + 1),
               Notes = string.Format("Notes {0}", i + 1),
               Done = false
          });
     }
     watch.Stop();

     Log += $"SQLite: INSERT 1000 items in {watch.ElapsedMilliseconds} milliseconds\n";

     watch.Restart();
     for (int i = 0; i < 1000; i++)
     {
          _realmService.Insert(new RealmTodoItem
          {
               Id = i + 1,
               Name = string.Format("Name {0}", i + 1),
               Notes = string.Format("Notes {0}", i + 1),
               Done = false
          });
      }
      watch.Stop();

      Log += $"Realm: INSERT 1000 items in {watch.ElapsedMilliseconds} milliseconds\n";
}
[/sourcecode]

Insertamos 1000 elementos en cada una de las base de datos con los siguientes resultados de media:

  • SQLite: 2400 milisegundos
  • Realm 1800 milisegundos

A la hora de obtener los 1000 registros almacenados:

[sourcecode language=»vb»]
private async void QueryCommandExecute()
{
     var watch = System.Diagnostics.Stopwatch.StartNew();
     var sqliteResult = await _sqliteService.GetAll();
     watch.Stop();

     Log += $"SQLite: QUERY 1000 items in {watch.ElapsedMilliseconds} milliseconds\n";

     watch.Restart();
     var realmResult = _realmService.GetAll();
     watch.Stop();

     Log += $"Realm: QUERY 1000 items in {watch.ElapsedMilliseconds} milliseconds\n";
}
[/sourcecode]

Los tiempos de media son:

  • SQLite: 50 milisegundos
  • Realm: 2 milisegundos

Por último, llegamos al momenos de eliminar registros:

[sourcecode language=»vb»]
private async void DeleteCommandExecute()
{
     var sqliteResult = await _sqliteService.GetAll();
     var watch = System.Diagnostics.Stopwatch.StartNew();
     foreach (var item in sqliteResult)
     {
          await _sqliteService.Remove(item);
     }
     watch.Stop();

     Log += $"SQLite: DELETE 1000 items in {watch.ElapsedMilliseconds} milliseconds\n";

     var realmResult = _realmService.GetAll();
     watch.Restart();
     foreach (var item in realmResult)
     {
          _realmService.Remove(item);
     }
     watch.Stop();

     Log += $"Realm: DELETE 1000 items in {watch.ElapsedMilliseconds} milliseconds\n";
}
[/sourcecode]

Los resultados son:

  • SQLite: 2300 milisegundos
  • Realm: 1300 milisegundos
Resultados rendimiento
Resultados rendimiento

Los tiempos en general en Realm son más reducidos que con SQLite, por lo que podemos decir que efectivamente el rendimiento en general es mejor. Optimizando (transacciones, etc) podemos mejorar ligeramente el rendimiento en SQLite dejando los resultados aún más cercanos. Realm hace mejor el trabajo sin nada especial adicional.

Ganador: Realm

Documentación

Realm ha creado una documentación específica para desarrolladores Xamarin bastante completa. Sin embargo, si comparamos con SQLite y debido fundamentalmente a la maduración y uso de cada uno de ellos, SQLite cuenta con mayor documentación. Xamarin cuenta con documentación y ejemplos utilizando SQLite sumado a la comunidad a nivel de artículos y ejemplos, hace que hoy por hoy, el acceso y aprendizaje de cada opción este mejor documentada en SQLite.

Ganador: SQLite

Mantenimiento

Ya hemos revisado aspectos importante relacionados con el arranque en cada uno de ellos, el acceso inicial y facilidad de uso. Sin embargo, creo bastante positivo el análisis del mantenimiento en cada caso en proyectos de peso.

El primer punto de impacto radica en la forma de trabajo con modelos de Realm. Dado que deben heredar de RealmObject y la creación debe realizarse utilizando Realm.CreateObject impidiendo la creación mediante un constructor simple. Igualmente en listas se utiliza RealmList en lugar de POCOs como hace SQLite que sencillamente añade ciertas etiquetas para saber como trabajar correctamente en cada caso. De cara a una correcta gestión de dependencias y testing podemos llegar a tener problemas con Realm necesitando una duplicidad de modelos con sencillos POCOs. Es posible aunque afecta sin duda al punto de facilidad de uso.

El segundo punto importante que puede afectar es la falta de soporte a Async en Realm. Damos por hecho que se añadirá soporte en futuras versiones de Realm, sin embargo, en la versión actual la falta de métodos asíncronos puede afectar en la gestión y mantenimiento de nuestro código.

Ganador: SQLite

Conclusiones

Llegados a este punto, cuatro evaluciones, dos con SQLite como vencedor, otras dos para Realm. ¿Qué usar?. La respuesta en este caso, como en la mayoría de casos, es depende. Realm se muestra como una interesante alternativa diría que perfectamente válida en pequeños proyectos con una cantidad baja de modelos. Su facilidad de arranque, uso y rendimiento lo hacen idóneo. Sin embargo, ante proyectos de peso con un gran mantenimiento la orientación al menos por ahora, seguiría decantándose por SQLite. El apoyo de los principales actores en el ambito móvil y la comunidad, hace que sea la opción más madura, sólida y documentada disponible.

Más información

[Xamarin.Android] De VSTS a HockeyApp, despliegue continuo

Upload - 02Introducción

La calidad en el software es algo innegociable. Un buen proceso en el desarrollo y gestión del proceso es fundamental para conseguir ese objetivo. Debemos entregar Apps móviles perfectamente adaptadas a cada plataforma, ofreciendo la mejor experiencia de usuario posible pero sobretodo, funcional. Una App funcional debe cubrir y cumplir unos mínimos exigentes de calidad.

Como desarrolladores, somo humanos y el código no estara libre de errores. Sin embargo, el proceso que apliquemos para la detección y corrección a los mismos, es vital.

Realizar un proceso automático en cada nuevo checkin o de manera programada donde:

  • Compilar proyectos.
  • Pasar pruebas unitarias.
  • Pasar pruebas de interfaz de usuario.
  • Incluso publicar automáticamente paquetes y resultados.

Nos permitirá detectar problemas en el código de la forma más prematura posible, pudiendo ofrecer mayor calidad. En este artículos vamos a repasar todo lo necesario para realizar tanto integración continua como entrega continua utilizando VSTS y HockeyApp.

HockeyApp
HockeyApp

Integración Continua

La integración continua consiste en hacer integraciones automáticas de un Proyecto lo más a menudo possible para así detector fallos cuantos antes.

Entendemos por integración la compilación y ejecución de pruebas.

WorkFlow
WorkFlow

El flujo del proceso sería:

  1. El desarrollador trabaja en su equipo de desarrollo subiendo cambios al repositorio de código donde estaría el código además de las pruebas unitarias.
  2. De una forma automática, el repositorio de código envia el mismo al servidor de BUILD.
  3. El servidor de BUILD realizará la compilación de la solución o proyectos, ejecutará pruebas y recopilará la información de los resultados.
  4. Los resultados los obtiene el desarrollador para poder analizarlos y actuar en consecuencia.

“La integración continua no evitará que se produzcan bugs, pero si nos permite encontrarlos y solucionarlos de una forma dramáticamente más fácil”

Martin Flowler

Los beneficios de utilizar integración continua son:

  • Detectar errores con mayor rapidez y antelación. Esto provocará que sea más sencillo de corregir y por lo tanto más barato.
  • Consistencia. O lo que es lo mismo, tener Builds reproducibles. La App funcionará en cualquier entorno con las mismas condiciones. Evitamos la “contaminación de la Build” o lo que es lo mismo, tener la Build con parámetros “a fuego” y condiciones específicas de la máquina de Build.
  • Poder automatizar también la entrega consiguiendo una entrega continua.

La configuración en VSTS

Ahora, utilizando Visual Studio Team Services en combinación con HockeyApp, podemos conseguir tener no solo interación continua, también entrega continua de forma bastante sencilla.

En este artículo nos centraremos en el despliegue de aplicaciones Android realizadas con Xamarin. La configuración en iOS es realmente similar salvando las distancias de la necesidad de utilizar OSX para la compilación. En cuanto a aplicaciones UWP, tenemos ligeros cambios relacionados con el empaquetado de la aplicación.

Veremos otros tipos de aplicaciones en próximos artículos.

Los pasos necesarios

Comenzamos dirigiéndonos a la pestaña Build de VSTS y pulsamos sobre el botón para crear una nueva Build.

Crear nueva Build
Crear nueva Build

Tras pulsar el botón podremos elegir plantilla. VSTS cuenta con plantillas destinadas a diferentes tipos de proyectos con los pasos básicos necesarios en cada caso. En nuestro ejemplo, elegimos la plantilla Xamarin.Android.

Elegimos plantilla
Elegimos plantilla

A continuación, vamos a elegir las opciones básicas de configuración como el repositorio donde se accederá al código, la rama y la máquina a utilizar.

Configuración básica
Configuración básica

Tras añadir la plantilla, varios pasos se crearán por defecto.

Pasos por defecto
Pasos por defecto

El primer paso restaurará los paquetes NuGet utilizados en el proyecto. El siguiente paso lógico sería la compilación del proyecto. Sin embargo, para poder realizar la compilación necesitamos previamente validar la cuenta Xamarin. Tendremos pasos para activar y desactivar la misma.

NOTA: El campo donde añadir la contraseña es un texto plano. Si la configuración es compartida o se desea enseñar, se vería la clave. Para solucionar el problema podemos añadir una variable llamada por ejemplo «XamarinPassword» y utilizarla posteriormente accediendo a ella $(XamarinPassword).

Salvo que tengamos proyecto de pruebas de UI, borraremos el paso de compilación del proyecto de test junto al paso de pasar las pruebas en Xamarin Test Cloud.

Tenemos todo lo necesario para compilar el proyecto Android, pero nos falta lo necesario para compilar la librería portable donde tenemos todo el código común compartido. Para ello vamos a añadir más pasos.

Añadimos un primer paso para restaurar paquetes NuGet.

Restaurar paquetes NuGet
Restaurar paquetes NuGet

A continuación, añadimos un paso encargado de compilar la librería portable.

Compilar la librería portable
Compilar la librería portable

El orden de cada paso debe ser algo similar a la siguiente captura.

Orden de los pasos de la Build
Orden de los pasos de la Build

Cada paso en detalle

Comenzamos restaurando los paquetes NuGet. La única configuración necesaria en este paso será realizar la selección de la solución.

Restaurar paquetes
Restaurar paquetes

NOTA: Este primer paso podría ser innecesario si no usas paquetes NuGet.

En la compilación de la librería portable (PCL) debemos establecer la configuración de compilación $(BuildConfiguration) además de seleccionar el .csproj de la PCL.

Compilar PCL
Compilar PCL

Llegamos al paso de mayor importancia, la compilación del proyecto Android. Debemos asegurarnos de tener establecida la configuración de compilación $(BuildConfiguration) junto al directorio de salida $(build.binariesdirectory)\$(BuildConfiguration).

Compilación Android
Compilación Android

Todo listo. Podemos enviar una Build a cola y obtendremos la compilación del proyecto, la creación del APK y su firma. El siguiente paso sería la subida y distribución utilizando HockeyApp.

La configuración de los triggers

En la pestaña de Triggers podemos configurar cuándo se lanza la Build. Tenemos la opción de establecer una opción de modo de integración continua, cada vez que se añadan cambios a la rama utilizada se lanzaría la Build o bien programar una Build a una fecha y/o hora específica.

Configuración de Triggers
Configuración de Triggers

HockeyApp

Llegamos al segundo paso de peso, la distribución continua con HockeyApp.

HockeyApp es una herramienta que facilita la gestión y distribución de aplicaciones móviles para iOS, Android y Windows. Ofrece además la gestión de analíticas, errores y feedback de parte de los usuarios. Comprada por Microsoft, es la opción idóneo tanto para la gestión de analíticas como para la distribución de betas a clientes.

La configuración en HockeyApp

Tras crear una cuenta si no tenías una ya disponible (es un proceso rápido y gratuito hasta dos aplicaciones) procedemos a crear una nueva aplicación.

Nueva App en HockeyApp
Nueva App en HockeyApp

En ese momento nos pedirá paquete. Sin embargo, subiremos el paquete automáticamente desde VSTS, por lo tanto, elegimos la opción de subida manual. A continuación, establecemos el nombre de la aplicación, la plataforma Android en este caso y el canal de distribución, normalmente beta aunque podría ser otra opcion.

Tras crear la aplicación, tendremos acceso a la información que necesitamos para subir el paquete, su identificador.

Identificador App
Identificador App

Para poder subir automáticamente el paquete de la aplicación a la aplicación creada en HockeyApp, necesitamos de alguna forma enlazar una herramienta con otra. Es un proceso sencillo.

API token
API token

Usaremos ese Token desde VSTS. Primero vamos a instalar la extensión de HockeyApp disponible en el marketplace en VSTS.

Extensión VSTS HockeyApp
Extensión VSTS HockeyApp

Tras instalar la extensión nos dirigimos a la configuración de nuestro VSTS. Concretamente a la pestaña de servicios para añadir un nuevo endpoint.

Endpoint HockeyApp
Endpoint HockeyApp

Añadimos un nombre descriptivo junto al API Token que generamos en HockeyApp y todo listo.

Despliegue continuo

Regresamos a VSTS para configurar el despliegue continuo. Nos dirigimos a la pestaña Release. Creamos una nueva definición de Release.

Nueva Release
Nueva Release

En la pestaña de artefactos hacemos la vinculación con nuestra Build.

Artifacts
Artifacts

Por último, añadimos una tarea del tipo HockeyApp.

HockeyApp
HockeyApp

Configuramos la tarea, seleccionamos la conexión que creamos previamente en la configuración de VSTS, añadimos el identificador de la aplicación creada en HockeyApp y la ruta al binario.

Configuración de la tarea
Configuración de la tarea

Casi lo tenemos todo preparado. Bastará con dirigirnos a la pestaña de Triggers para seleccionar la opción Continuous Deployment. Cada vez que se compile y genere un paquete, subir a HockeyApp (es totalmente personalizable, se puede programar una fecha y hora, lanzar de forma manual, etc.).

Más información

[Xamarin] Utilizando SQLite

Database-WFIntroducción

El trabajo con datos en dispositivos móviles se ha convertido ya en algo común y habitual en el desarrollo de aplicaciones. Existe una gran variedad de tipos de datos y formas de almacenamiento:

  • Archivos de texto. Texto plano o html cacheado en el espacio de almacenamiento aislado de la aplicación.
  • Imágenes. En el espacio de almacenamiento aislado de la aplicación o almacenadas en directorios conocidos del sistema.
  • Archivos serializados. Archivos XML o Json con objetos serializados.
  • Bases de datos. Cuando se requieren datos estructurados, obtener información más compleja con consultas avanzadas entre otro tipo de necesidades, la posibilidad de las bases de datos es la elección idónea.

Las ventajas de utilizar una base de datos son múltiples:

  • Almacenamiento estructurado con eficacia alta.
  • Posibilidad de utilizar consultas y aplicar filtros.
  • Posibilidad de reutilizar conocimientos de base de datos en la gestión de datos en nuestras aplicaciones móviles.

Introducción a SQLite

SQLite es un motor de base de datos Open Source utilizado en todas las plataformas móviles y adoptado tanto por Apple como Google como Microsoft. El uso de SQLite en aplicaciones móviles es una gran opción ya que:

  • La base de datos es pequeña y fácil de portar.
  • La base de datos se concentra en un pequeño archivo.
  • Implementa la mayor parte del estándar SQL92.

Preparando el entorno

Comenzamos creando una aplicación Xamarin.Forms utilizando una librería portable (PCL):

Nueva aplicación Xamarin.Forms
Nueva aplicación Xamarin.Forms

Tras crear la aplicación, añadimos las carpetas básicas para aplicar el patrón MVVM además del paquete NuGet de Unity para la gestión del contenedor de dependencias.

Estructura del proyecto
Estructura del proyecto

Con el proyecto y estructura base creada, vamos a añadir SQLite al proyecto. Realm esta disponible en NuGet. Vamos a añadir en cada proyecto de la solución la última versión disponible del paquete utilizando NuGet. El paquete a utilizar es SQLite.Net PCL, implementación Open Source compatible con librerías portables (PCL) con soporte a .NET y Mono.

SQLite.Net PCL
SQLite.Net PCL

Tras añadir la referencia vamos a crear una interfaz que defina como obtener la conexión con la base de datos y abstraer la funcionalidad específica de cada plataforma. Trabajando con SQLite, el único trabajo específico a implementar en cada plataforma es determinar la ruta a la base de datos y establecer la conexión.

public interface ISQLite
{
     SQLiteAsyncConnection GetConnection();
}

En Android, la implementación de ISQLite nos permite establecer la conexión con la base de datos.

[assembly: Dependency(typeof(SQLiteClient))]
namespace TodoSqlite.Droid.Services
{
    public class SQLiteClient : ISQLite
    {
        public SQLiteAsyncConnection GetConnection()
        {
            var sqliteFilename = "Todo.db3";
            var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);

            var path = Path.Combine(documentsPath, sqliteFilename);

            var platform = new SQLitePlatformAndroid();

            var connectionWithLock = new SQLiteConnectionWithLock(
                                         platform,
                                         new SQLiteConnectionString(path, true));

            var connection = new SQLiteAsyncConnection(() => connectionWithLock);

            return connection;
        }
    }

}

NOTA: Utilizamos el atributo assembly:Dependency para poder realizar la resolución de la implementación con DependencyService.

En iOS, la implementación de ISQLite nos permite establecer la conexión con la base de datos. El archivo de la base de datos lo situamos dentro de la carpeta Library dentro del espacio de almacenamiento de la aplicación.

[assembly: Dependency(typeof(SQLiteClient))]
namespace TodoSqlite.iOS.Services
{
    public class SQLiteClient : ISQLite
    {
        public SQLiteAsyncConnection GetConnection()
        {
            var sqliteFilename = "Todo.db3";
            var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            var libraryPath = Path.Combine(documentsPath, "..", "Library");
            var path = Path.Combine(libraryPath, sqliteFilename);

            var platform = new SQLitePlatformIOS();

            var connectionWithLock = new SQLiteConnectionWithLock(
                                          platform,
                                          new SQLiteConnectionString(path, true));

            var connection = new SQLiteAsyncConnection(() => connectionWithLock);

            return connection;
        }
    }
}

Todo listo para comenzar!

La definición de modelos

En nuestra aplicación, trabajaremos con elementos del listado ToDo, una única entidad sencilla.

public class TodoItem
{
     [PrimaryKey, AutoIncrement]
     public int Id { get; set; }
     public string Name { get; set; }
     public string Notes { get; set; }
     public bool Done { get; set; }
}

La gestión de campos especiales o relacionados las gestionamos mediante el uso de etiquetas. En nuestro ejemplo establecemos el campo Id como clave primaria gracias a la etiqueta PrimaryKey y además que autoincremente con el uso de AutoIncrement.

La interfaz de usuario

En nuestra aplicación contaremos con dos vistas, un listado de tareas y una vista de detalles para crear, editar o eliminar una tarea específica.

Comenzamos definiendo la vista principal. Tendremos un listado de tareas:

<ListView 
    ItemsSource="{Binding Items}" 
    SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <ViewCell.View>
            <StackLayout 
              Padding="20,0,20,0"                       
              Orientation="Horizontal"       
              HorizontalOptions="FillAndExpand">
              <Label Text="{Binding Name}"
                     VerticalTextAlignment="Center"
                     HorizontalOptions="StartAndExpand" />
              <Image Source="check.png"
                     HorizontalOptions="End"
                     IsVisible="{Binding Done}"/>
            </StackLayout>
          </ViewCell.View>
        </ViewCell>
      </DataTemplate>
     </ListView.ItemTemplate>
</ListView>

A parte de definir como se visualizará cada elemento de la lista definiendo el DataTemplate establecemos la fuente de información, propiedad ItemsSource enlazada a propiedad de la ViewModel que obtendrá los datos de la base de datos.

Además del listado, debemos añadir en nuestra interfaz una forma de poder insertar nuevas tareas. Para ello, una de las opciones más habituales e idóneas es utilizar una Toolbar.

<ContentPage.ToolbarItems>
    <ToolbarItem Name="Add" 
                 Command="{Binding AddCommand}"  >
      <ToolbarItem.Icon>
        <OnPlatform x:TypeArguments="FileImageSource"
                    Android="plus"
                    WinPhone="Assets/add.png" />
      </ToolbarItem.Icon>
    </ToolbarItem>
</ContentPage.ToolbarItems>

Añadimos un ToolbarItem que permitirá añadir elementos.

La clase Device es muy importante en Xamarin.Forms ya que nos permite acceder a una serie de propiedades y métodos con el objetivo de personalizar la aplicación según dispositivo y plataforma. Además de permitirnos detectar el tipo de dispositivo, podemos detectar la plataforma gracias a la enumeración Device.OS o personalizar elementos de la interfaz gracias al método Device.OnPlatform entre otras opciones. En nuestro ejemplo, personalizamos el icono de añadir en base a la plataforma.

Nuestra interfaz:

Vista principal
Vista principal

Enlazamos la View con la ViewModel estableciendo una instancia de la ViewModel a la propiedad BindingContext de la página.

BindingContext = App.Locator.TodoItemViewModel;

En la ViewModel contaremos con una propiedad pública para definir el listado de tareas, además de la tarea seleccionada (utilizada para la navegación):

private ObservableCollection<TodoItem> _items;
private TodoItem _selectedItem;

public ObservableCollection<TodoItem> Items
{
     get { return _items; }
     set
     {
          _items = value;
          RaisePropertyChanged();
     }
}

public TodoItem SelectedItem
{
    get { return _selectedItem; }
    set
    {
          _selectedItem = value;
    }
}

Añadimos elementos con un comando disponible en la ViewModel.

private ICommand _addCommand;

public ICommand AddCommand
{
     get { return _addCommand = _addCommand ?? new DelegateCommand(AddCommandExecute); }
}

private void AddCommandExecute()
{

}     

Al pulsar y lanzar el comando, navegaremos a la vista de detalles.

_navigationService.NavigateTo<TodoItemViewModel>(_selectedItem);

Si creamos un nuevo elemento pasaremos como parámetro una nueva entidad de TodoItem, en caso de seleccionar una existente, pasaremos el seleccionado disponible en la propiedad SelectedItem.

Definimos la interfaz de la vista de detalles:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TodoRealm.Views.TodoItemView"
             Title="{Binding Name}">
  <StackLayout 
    VerticalOptions="StartAndExpand" 
    Padding="20">
    <Label 
      Text="Name" />
    <Entry 
      Text="{Binding Name}"/>
    <Label 
      Text="Notes" />
    <Entry 
      Text="{Binding Notes}"/>
    <Label 
      Text="Done" />
    <Switch 
      x:Name="DoneEntry" 
      IsToggled="{Binding Done, Mode=TwoWay}"/>
    <Button 
      Text="Save"
      Command="{Binding SaveCommand}"/>
    <Button 
      Text="Delete"
      Command="{Binding DeleteCommand}"/>
    <Button 
      Text="Cancel"
      Command="{Binding CancelCommand}"/>
  </StackLayout>
</ContentPage>

Añadimos cajas de texto para poder editar toda la información de una tarea además de botones para poder guardar, borrar o cancelar y navegar atrás.

El resultado:

Detalle
Detalle

Para enlazar la información de un elemento seleccionado, debemos capturar la información enviada en la navegación. Creamos una propiedad pública para enlazar con la UI de la tarea:

private TodoItem _item;

public TodoItem Item
{
     get { return _item; }
     set { _item = value; }
}

¿Cómo capturamos el elemento seleccionado en la navegación?. Utilizamos el método OnAppearing para capturar el parámetro NavigationContext.

public override void OnAppearing(object navigationContext)
{
     var todoItem = navigationContext as TodoItem;

     if (todoItem != null)
     {
          Item = todoItem;
     }

     base.OnAppearing(navigationContext);
}

En cuanto a cada botón, cada uno de ellos estará enlazado a un comando:

private ICommand _saveCommand;
private ICommand _deleteCommand;
private ICommand _cancelCommand;

public ICommand SaveCommand
{
     get { return _saveCommand = _saveCommand ?? new DelegateCommand(SaveCommandExecute); }
}

public ICommand DeleteCommand
{
     get { return _deleteCommand = _deleteCommand ?? new DelegateCommand(DeleteCommandExecute); }
}

public ICommand CancelCommand
{
     get { return _cancelCommand = _cancelCommand ?? new DelegateCommand(CancelCommandExecute); }
}

private void SaveCommandExecute()
{

}

private void DeleteCommandExecute()
{

}

private void CancelCommandExecute()
{

}

Trabajando con SQLite

Para trabajar con la base de datos utilizaremos DependencyService para obtener la implementación de ISQLite y obtener una conexión.

private SQLiteAsyncConnection _sqlCon;
_sqlCon = DependencyService.Get<ISQLite>().GetConnection();

Para almacenar nuestras tareas, comenzamos creando la tabla necesaria en la base de datos.

public async void CreateDatabaseAsync()
{
     using (await Mutex.LockAsync().ConfigureAwait(false))
     {
          await _sqlCon.CreateTableAsync<TodoItem>().ConfigureAwait(false);
     }
}

Utilizamos el método CreateTableAsync<>() para crear la tabla.

Continuamos con las operaciones básicas de CRUD. Para obtener la información almacenada en una tabla podemos acceder a la tabla y obtener el listado utilizando el método ToListAsync.

public async Task<IList<TodoItem>> GetAll()
{
     var items = new List<TodoItem>();
     using (await Mutex.LockAsync().ConfigureAwait(false))
     {
          items = await _sqlCon.Table<TodoItem>().ToListAsync().ConfigureAwait(false);
     }

     return items;
}

NOTA: Podemos realizar consultar SQL utilizando el método QueryAync.

A la hora de insertar, verificamos si estamos ante un registro existente o no, para realizar el registro de un nuevo elemento o actualizar uno existente con los métodos InsertAsync o UpdateAsync respectivamente.

public async Task Insert(TodoItem item)
{
     using (await Mutex.LockAsync().ConfigureAwait(false))
     {
          var existingTodoItem = await _sqlCon.Table<TodoItem>()
                        .Where(x => x.Id == item.Id)
                        .FirstOrDefaultAsync();

          if (existingTodoItem == null)
          {
               await _sqlCon.InsertAsync(item).ConfigureAwait(false);
          }
          else
          {
               item.Id = existingTodoItem.Id;
               await _sqlCon.UpdateAsync(item).ConfigureAwait(false);
          }
     }
}

Eliminar es una acción sencilla realizada con el método DeleteAsync.

public async Task Remove(TodoItem item)
{
     await _sqlCon.DeleteAsync(item);
}

Tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

Más información

[Material SVQXDG] Xamarin University Spring Fling 2016

600_449053644.jpegEl evento

El pasado 24 de Mayo, desde el grupo SVQXDG, grupo de desarrolladores Xamarin de Sevilla, organizábamos el Xamarin University Spring Fling 16.

Xamarin University Sprint Fling 2016
Xamarin University Sprint Fling 2016

Evento celebrado a nivel global por las comunidades Xamarin a lo largo de todo el mundo. En SVQXDG la idea era reunir estudiantes de Xamarin University, repasar las últimas novedades presentadas en el Xamarin Evolve y compartir experiencias y networking entre todos como viene siendo habitual además de repartir algun que otro regalo gracias al equipo de Xamarin University.

El material

Hicimos un breve repaso de que es, funcionamiento y estado actual de Xamarin University. Para llegar al plato fuerte donde de la forma más práctica posible vimos las grandes novedades del Evolve:

  • Editor visual de Xamarin.Forms
  • Simulador remoto de iOS en Windows
  • Xamarin.Forms Themes y DataPages
  • Uso controles nativos en Xamarin.Forms
  • Etc

En cuanto a las demos técnicas realizadas, las tenéis disponible en GitHub:

Ver GitHub

Quisiera terminar añadiendo algunos agradecimientos a Josué Yeray y Marcos Cobeña por participar junto a un servidor en las sesiones y por supuesto, muchas gracias a todos los asistentes.

Más información

[Evento SVQXDG] Xamarin University Spring Fling 16

XamarinEl evento

En los últimos años, Xamarin ha ido celebrando cada vez con mayor expectación y éxito el Evolve. Un gran evento repleto de grandes sesiones técnicas, networking y agún que otro anuncio. Este año no será menos, además será más especial si cabe.

Y ante un evento de este calibre y con estas posibilidades, ¿algo mejor que tomar las novedades principales y montar un gran evento?. así nace este evento, completo de grandes sesiones técnicas abordando todas las novedades y tiempo para el networking. Por si fuese poco, daremos información detallada de Xamarin University y tendremos grandes sorpresas para todos los asistentes (si todos!).

La agenda será la siguiente:

  • 18:30h-18:45h: Recepción y bienvenida.
  • 18:45h-20:00h: Demos y más demos de las novedades del Evolve. Veremos Xamarin.Forms Previewer y su uso con datos de prueba en diseño, demos de los temas o DataPages incluidos en Xamarin.Forms, demos potentes con Xamarin Workbooks y mucho más. Si quieres ver y conocer todas las novedades, no te lo puedes perder!
  • 20:00h-20:20h: Mesa redonda donde se trasmitirán sesiones interesantes, experiencias en la visita al Evolve, respuestas a preguntas y lo que pueda surgir.
  • 20:20h-20:30h: Xamarin University  y concurso de regalos!

El lugar

El evento se celebrará en la ETS de Ingeniería Informática. Dirección detallada:

E.T.S. Ingeniería Informática – Universidad de Sevilla
Av. Reina Mercedes s/n
Sevilla Se 41012

ETS de Ingeniería Informática
ETS de Ingeniería Informática

La sala será la A2.14.

La fecha

El evento tendrá lugar el próximo Martes, 24 de Mayo de 18:30h a 20:30h. Tendremos una sesión técnica de  75 minutos de duración junto a una mesa redonda de 30 minutos.

¿Te apuntas?

Más información