Developer // Telecomunicaciones // Capoeira // Me gusta aprender de los que están cambiando vidas.

2013/08/30

Historia de una App: un mes en la Tienda Windows y en Google Play

Les cuento que un mes después del lanzamiento de la app "Calendario Tributario Colombia" para Android, sólo está instalada en 50 equipos. Muy poco comparada con "Calendario Tributario Colombiano" para Windows 8, que el primer mes alcanzó 104 descargas y 8 meses después tiene 1250 – igual es poco, pero vamos avanzando –. Lamentablemente no tengo el dato de cuántos usuarios todavía la tienen instalada en Windows 8, como sí pasa con la de Android: 50/60.

Pienso que eso sucede por 2 razones:

1. Las políticas de la Tienda Windows son mucho más acertadas que las de Google Play. Cuando buscan en la categoría de finanzas de la Tienda Windows la aplicación aparece en el segundo lugar – o solía estarlo –, lo que demuestra una clara segmentación de mercado (Colombia); mientras que en Google Play aparece en la posición 106, por debajo de apps como “Banesco móvil” y “Bank of America”, entidades que no ofrecen sus servicios en el país. Quiero resaltar que la app para Windows 8 se demoró 2 semanas en ser aprobada y publicada, mientras que la de Android estaba disponible en la tienda 3 horas después del registro.

2. Microsoft hace un esfuerzo inmenso para darles visibilidad a los desarrolladores, al punto de tener equipos enteros dedicados a la difusión de nuevas tecnologías (DPE). Si la aplicación es buena, seguro será difundida por cientos de canales (redes sociales, eventos, prensa, etc.). Es evidente que eso no pasa con Google, que ni siquiera se toma la molestia de revisar las apps antes de publicarlas.

Algunos dirán que Windows 8 es nuevo, que hay muy pocas aplicaciones y que por eso la app tiene más visibilidad. Yo les digo que Android es viejo, tiene más usuarios y que la cuota de mercado, por pequeña que sea, debería superar a la de Windows.

Ya tengo adelantada la versión paga de la aplicación, con características adicionales que espero sean de su agrado. Por supuesto, saldrá primero para Windows 8.

2013/07/03

El nuevo sistema de autenticación de ASP.NET

Microsoft es sin duda la empresa que mejor documentación tiene sobre sus tecnologías de desarrollo, y cuenta con la mejor herramienta: Visual Studio. Sin embargo hay un tema con el que nunca me había sentido satisfecho hasta hoy: la autenticación de ASP.NET y su integración con el modelo de negocio.

ASP.NET (MVC 4) ofrece un sistema de autenticación 'out of the box' muy útil, pero que en mi opinión carece de documentación que explique la mejor forma de integrarlo con otras entidades, entendiendo integración como la creación de relaciones uno a muchos. Estas son algunas propuestas que hay en internet y la razones por las que no me gustan:

1. Crear un proveedor personalizado que incluya las relaciones. Además de ser tedioso y susceptible a errores, implica desaprovechar lo que otros desarrolladores de mucha experiencia han hecho por nosotros.

2. Crear dos bases de datos, una para los usuarios y otra para el negocio (en esa parte estoy de acuerdo), y duplicar la información de los usuarios, asegurándose de crearlos con el mismo Id en ambas partes. Ese parece un buen acercamiento, excepto porque la base de datos que más utilizo, SQL Azure, no soporta transacciones distribuidas para asegurar que en ambos repositorios se va a crear correctamente el item. Entiendo que eso se podría solucionar con un trigger, pero la verdad es que no tengo ni idea de esas estructuras y soy fan de Code First.

3. No complicarse la vida, dejar todo en la misma base de datos y esperar que Code First haga su trabajo. Esa me encanta, pero en versiones anteriores de ASP.NET hay un inconveniente: la autenticación no es compatible con Code First, por lo que el "no complicarse la vida" cambiaba drásticamene por la necesidad de construir la base de datos a mano.

Es en el último punto que la nueva versión de ASP.NET la saca del estadio: el nuevo sistema de autenticación es 100% compatible con Code First; en el código fuente se puede ver el IdentityDbContext con sus respectivos DbSets para usuarios, roles y cuentas asociadas. Atrás quedó la SimpleMembership y su SimpleMembershipInitializer, y ni hablar de SQLMembership, que impedían a Code First hacer su trabajo.

Ahora escribir una relación es tan sencillo como:

public class Element
{
     [Key]
     [ScaffoldColumn(false)]
     public int Id { get; set; }
     public string Name { get; set; }
     public User User { get; set; }

}

Y basta con agregar una línea al DbContext:

public DbSet<WebApplication.Models.Element> Element { get; set; }

Incluso no es necesario agregarla a mano, porque el Scaffolding está en Click derecho > Agregar > Scaffold.

Un Create queda reducido a:

var usr = db.Users.Find(User.Identity.GetUserId());
element.User = usr;
db.Element.Add(element);
db.SaveChanges();

Porque ya le podemos decir a User.Identity que nos pase el Id del usuario gracias a una extensión que pueden encontrar en App_Start.IdentityConfig. Nada que ver con el anticuado Membership.GetUser(User.Identity.Name). Basta con agregar:

using Microsoft.AspNet.Identity

Y para los que están pensando en el escenario 2 existen las Code First Migrations, que funcionan muy bien cuando tenemos cambios en el modelo. Se habilitan con el comando:

Enable-Migrations -ContextTypeName Application.Models.DbContext

Pueden usar la nueva versión de ASP.NET (MVC 5) con Visual Studio 2013 y explorar las novedades en este tutorial. ¡Se los recomiendo! yo estoy muy satisfecho con los cambios... y eso que todavía está en beta.

2013/02/19

Metadatos de Azure vs. caracteres especiales

Estoy desarrollando una aplicación que guarda documentos en Windows Azure y quiero compartir dos decisiones de diseño:

  • No utilizo tablas que relacionen usuarios y documentos, sino que creo un contenedor para cada usuario. Eso me da mayor flexibilidad al momento de hacer transacciones o al eliminar los usuarios (tarea muy frecuente en fase de desarrollo).
  • Los datos asociados al documento como fechas, descripción, etc. los almaceno en los metadatos del blob.

Hay un problema con la última decisión: no se pueden almacenar caracteres especiales en los metadatos. Para solucionarlo, seguí la recomendación de WarNov y utilicé un codificador HTML:

blob.Metadata["Description"] = HttpUtility.HtmlEncode(model.Description);
Description = HttpUtility.HtmlDecode(blob.Metadata["Description"]);

Los nombres de los archivos tampoco pueden contener espacios porque hay navegadores que no los aceptan en las cabeceras de tipo Content-Disposition, necesarias para descargarlos. Yo los reemplazo por "_":

blob.Metadata["FileName"] = 
    HttpUtility.HtmlEncode(file.FileName.Replace(" ", "_"));
Response.AddHeader("Content-Disposition", "attachment; filename=" + 
    HttpUtility.HtmlDecode(blob.Metadata["FileName"]));