lunes, 23 de diciembre de 2013

Respaldo de la base de datos Offline en iTunes

El escenario principal para las aplicaciones Offline que generamos con GeneXus, se tiene un componente en el servidor donde se centralizan los datos ingresados en cada uno de los dispositivos.

En este escenario no es imprescindible hacer un respaldo de la base de datos Offline, ya que la misma siempre se puede volver a construir a partir de los datos del servidor.

Sin embargo, el generador Offline permite otro escenario donde sí puede ser importante tener respaldo de los datos: cuando la aplicación es completamente Offline y no tiene componente en el servidor.

Si bien acceder al sistema de archivos del iPhone se puede, no hay una forma "nativa" de hacerlo. Hay aplicaciones que exponen dicho sistema de archivos a la Mac o al PC, pero en general tienen algún costo.

En particular para la base de datos Offline, como se almacena en la carpeta Documents que es una carpeta especial dentro de la aplicación, es posible copiar el archivo desde iTunes como se explica en este documento de Apple.

Para lograr esto, simplemente debemos agregar una nueva clave al archivo .plist del proyecto generado (o se puede cambiar el template si lo vamos a querer siempre...). La clave que se debe agregar es UIFileSharingEnabled con valor YES. La forma de hacerlo está explicada en este documento.

Cabe aclarar que si se hace un respaldo completo del dispositivo mediante iTunes, este archivo también se respalda. Lo que se explica en esta nota es cómo respaldar solo este archivo si quisieramos hacerlo...

martes, 10 de diciembre de 2013

Licencias de software libre, ¿cuál usar?

Quisiera conocer la opinión de gente que sepa más que yo del tema de licencias de software libre, para saber cuál conviene usar.

Lo que quiero es distribuir algo(1) de la forma menos restrictiva posible, es decir, que quien lo quiera usar lo use para lo que más le convenga, y si lo quiere distribuir, vender, usar de ejemplo, o lo que sea, lo pueda hacer.

En la página de Wikipedia sobre el tema tiene varias de estas licencias, muchas de ellas no las conozco.

De las que aparecen ahí, las que más me convencen por las cosas que permiten y por ser conocidas son la de Apache, la de BSD y la de MIT.

También entiendo que existe el concepto de "copyleft" que no se muy bien como juega en todo esto.

Por el momento me inclino por la de MIT, pero acepto sugerencias :)


(1) Para que no quede demasiado misterioso, se trata de una KB GeneXus correspondiente a una aplicación para Smart Devices. Nada demasiado complicado, es de uso personal, pero que pienso puede servirle a alguien más... Ya habrá más novedades.

viernes, 15 de noviembre de 2013

Xcode: debug de aplicaciones que usan localización

Para la versión Beta 3 de GeneXus Tilo (a liberarse cerca de fin de año...), estuve trabajando en una funcionalidad que llamamos Proximity Alerts, que es parte de la Geolocation API.

No es el objetivo de esta nota explicar la funcionalidad, pero merece por lo menos un comentario. Consiste básicamente en definir regiones dadas por un punto geográfico y un radio en metros, y cuando el dispositivo ingresa a una de estas regiones se dispara un evento en la aplicación.

La funcionalidad en sí no es difícil de implementar, pero es bastante difícil hacer debug... Por suerte y gracias a Fabián, aprendí algunas cosas de Xcode y del simulador de iOS que facilitan un poco la tarea.

Xcode

En Xcode, en el Scheme de la aplicación (Product > Scheme > Edit Scheme... > tab Options) tenemos la opción de decirle a la aplicación que inice en una ubicación determinada, y también tenemos la posibilidad de pasarle un archivo GPX para que el simulador vaya cambiando de ubicación siguiendo una ruta.


Los archivos GPX son simplemente archivos XML con información geográfica, y se pueden crear de forma muy simple con una aplicación gratuita en el App Store llamada GPX Creator.

Otra configuración que está en el Scheme, pero esta vez en el tab Info, es que la aplicación no inicie automáticamente cuando le damos Run en Xcode, sino que espere a que el desarrollador la inicie de forma manual. Esto como dato anecdótico, porque no nos sirvió para lo que queríamos.

iOS Simulator

En el simulador también tenemos la opción de configurar la ubicación, pero una vez que la aplicación ya está corriendo. Esto se hace en la opción de menú Depurar > Ubicación (no se por qué Xcode está en inglés y el simulador en español...)

En este caso, las opciones son usar una ubicación predeterminada o usar una definida por nosotros.

La diferencia con la configuración en Xcode, es que entre las ubicaciones predeterminadas ya hay algunas que son una serie de puntos por los que va pasando.

Conclusión

Siempre es bueno conocer las herramientas que usamos y más cuando tienen funcionalidades que son útiles en casos puntuales y no son demasiado fáciles de encontrar. Por eso me pareció interesante compartir esto.

De todas formas, más allá de estas opciones que sirvieron para avanzar en la solución, hubo algo que no pudimos hacer: que el simulador se "moviera" en una ruta cuando la aplicación no está corriendo. Así que de todas formas para probar la funcionalidad tuve que salir a caminar con el teléfono...

lunes, 7 de octubre de 2013

Diapositivas y video de mi charla en el #GX23

El video de la charla lo pueden ver en el sitio del evento.

Las diapositivas están también ahí, pero también las subí a SkyDrive que funciona mejor que SlideShare, que no respeta el orden de aparición de los elementos de la diapositiva.

Solo las diapositivas capaz que no dicen mucho, pueden ver las notas en cada una (creo que están bien... no las revisé en la versión final).

jueves, 3 de octubre de 2013

Resumen del #GX23

Terminó otro encuentro GeneXus. Como siempre fueron tres días de mucha actividad, de ver cosas nuevas, de reencontrarse con gente, de salir de la rutina.

Otra vez, como todos los años, con una organización impecable. Todas las charlas (las que vi yo al menos) respetaron el horario. Lleva mucho esfuerzo hacer que todo salga bien, hacer que del otro lado ni siquiera nos demos cuenta de todo lo que hay atrás. Así que mis felicitaciones a quienes participaron de la organización.

Con respecto al contenido, hubo sin duda mucha variedad. Mi interés eran obviamente las charlas de Smart Devices, y en particular las que daba la comunidad.

Así es que fui a las charlas de DVelop (What 2 Listen, SD+), a la que dio Armando Cardozo de Simplifica sobre oportunidades y desafíos, y a la de Marcos Abellón y Matías Preciozzi sobre reconocimiento facial y realidad aumentada.

La conclusión de estas charlas, es que se están haciendo cosas muy interesantes con el generador, y que una aplicación exitosa puede surgir de una idea, de una necesidad personal, y lograr tener alcance mundial. Creo que la oportunidad para la comunidad está, la herramienta está, ideas seguro que hay, así que falta nada más animarse.

También hubo varias charlas interesantes dadas por gente de Artech. Si están en el tema creo que es casi obligatorio verlas. Quería destacar algunas que vi yo. La de Sebastián del Rio sobre las cosas que se pueden hacer de interfaz de usuario y la de Franklin Buitrón sobre user experience. También las de Pablo Mazzilli que contó el modelo atrás de las aplicaciones offline, y la de Leonardo Piñeyro que mostró como trabajar con aplicaciones offline en GeneXus, y se animó a hacer la demo en vivo :)

El otro tema que me interesaba era ver la opinión que generaba el tema de las aplicaciones offline, funcionalidad en la que estuvimos trabajando fuerte el último año. Por suerte las charlas que dimos sobre el tema fueron a sala llena, y además fue una de las cosas que más nos preguntaron en el cara a cara de Smart Devices y en las sesiones de "coaching".

A propósito del "coaching", fue una propuesta nueva en este evento que fue bien recibida ya que concurrió bastante gente. Hay cosas para mejorar, por ejemplo la forma de ordenarnos, o tratar de que no se convierta en un lugar donde reportar errores del generador, pero creo que estuvo bueno y que este tipo de actividades aportan valor.

Con respecto a mi charla me fue bastante bien, como decía vino mucha gente y después vino gente a hacerme preguntas y comentarios, por lo que género interés.

Así que a modo de resumen, la comunidad ya demostró que se pueden hacer cosas interesantes a partir de una idea, y me sirvió para confirmar que el tema que estamos trabajando despierta interés. Es decir, fue un muy buen evento.

¡Nos vemos en el #GX24! :)


Actualización, 3 de octubre de 2013, 13:42 - Corrección a los links para que apunten a los videos de las charlas.

sábado, 21 de septiembre de 2013

Charla de Offline Applications en el #GX23

Este año en el Encuentro GeneXus voy a estar dando una charla sobre aplicaciones Offline para dispositivos móviles.

Todavía no está la grilla con los horarios, pero pueden ver la descripción de la charla en la lista preliminar en el sitio del encuentro.

El tema de aplicaciones Offline es nuevo en GeneXus Tilo. Aunque ya había algo que se mostró en el evento pasado, este año está mucho más completa la funcionalidad, por lo que los invito a quienes les interese a ver las charlas sobre el tema.

Además de mi charla va a haber otras 3, que van a estar en la misma sala y una a continuación de la otra, así que se pueden agendar todo el "track". Pueden ver más información de estas charlas en los siguientes links:

viernes, 30 de agosto de 2013

Seguridad en aplicaciones GeneXus

En la reunión de ayer del GUG Montevideo, Diego Rostagnol hizo una presentación sobre seguridad en aplicaciones GeneXus que estuvo interesante.

La charla generó una discusión que estuvo buena, porque se plantearon posibles problemas de seguridad en distintos niveles de la aplicación. Diego planteó algunos casos que yo en particular no había considerado, y creo que los aportes del público sirvieron para aclarar otros que tal vez no estaban del todo correctos.

No me considero un experto en seguridad ni mucho menos, pero igual quería dejar escrito acá más o menos los temas que se hablaron ayer, más que nada porque creo que estos temas son muy importantes y en general no se le dedica el tiempo suficiente.

GeneXus resuelve una buena parte de los problemas de seguridad, y eso es muy bueno, pero también tiene un lado negativo: como GeneXus resuelve cosas de forma automática, podemos pensar que está todo resuelto. Y eso no es así.

Veamos los puntos que surgieron ayer de la reunión, y algún otro que puede haber faltado.

La propiedad Encrypt URL Parameters no evita que se pueda acceder a otros registros

Diego lo mostró ayer en una demo: una URL encriptada se puede desencriptar fácilmente si se conoce el SiteKey o SessionKey que se usa para encriptar.

El problema es que GeneXus usa una clave por defecto, que si no la cambiamos, cualquiera la conoce...

Ver la documentación en el Wiki por más información.

Un campo oculto o "disabled" en un Web Panel no garantiza que no se modifique

Recuerden que el HTML está del lado del cliente, y en general los navegadores permiten modificarlo.

Lo que mostró ayer Diego fue un filtro que el usuario no debería modificar porque no tiene permisos, pero que va como un combo con Enabled = false en el form del Web Panel, que igual se puede modificar y ver los datos que se supone que el usuario no tiene permisos.

Esto en realidad era así porque estaba mal resuelto, se tiene que hacer el control en el for each y no asumir que lo que viene del cliente es válido. Pero de todas formas, es interesante como ejemplo.

HTTPS es obligatorio

Es obligatorio por lo menos en dos lugares:
  • pantalla de login Web, o cualquier otro objeto web que tenga que mandar datos sensibles del usuario
  • servicios REST de las aplicaciones SD.
En realidad, hoy en día no hay ningún buen motivo para no poner HTTPS en toda la aplicación. En algún momento pudo haber diferencia de performance por agregarle el encriptado, pero hoy en día es algo mínimo.

GAM por si solo no resuelve la seguridad

El GAM es un gran avance con respecto a lo que teníamos antes, USENLO!

Lo que había antes, era cada uno programando su esquema de seguridad a mano. Pero igual, GAM por si solo, no resuelve el problema.

En particular, si no usamos HTTPS, cualquiera que pueda ver el tráfico entre el dispositivo y el servidor, puede "robar" el token de autenticación y usarlo sin haberse logueado.

Nunca guarden la contraseña del usuario en su aplicación

En serio, no lo hagan. Si están guardando contraseñas, lo están haciendo mal.

Hay otras formas de autenticar usuarios, contra Google, Facebook, Twitter, OpenId, o lo que sea...

Si de todas formas tienen que guardar usuarios y contraseñas, nunca, nunca, NUNCA, las guarden en texto plano. Tampoco usen algoritmos de encriptación, las contraseñas jamás se tienen que poder desencriptar.

La forma de guardar contraseñas es usando algún algoritmo de hash, pero averigüen antes cuál es seguro (MD5 no lo es), y si pueden apliquen el algoritmo varias veces y agregándole algún código extra que solo conoce la aplicación.

Pero de nuevo, seguramente si guardan contraseñas lo están haciendo mal...

Security Scanner

No se habló nada en la charla de ayer, pero hay una extensión en el Marketplace, que sirve para detectar posibles problemas de seguridad en las aplicaciones.

En particular no la he usado, pero se que hay gente que sí, y que sirve. Les recomiendo que por lo menos la miren y evaluen si les sirve.

Conclusión

La seguridad es un tema delicado.

Lo peor que podemos hacer es confiarnos que está todo resuelto, porque no es así. GeneXus ayuda mucho en varios puntos, pero tenemos que trabajar también nosotros para que las aplicaciones sean realmente seguras.
 

domingo, 18 de agosto de 2013

async/await en C# 5.0

Personalmente no he hecho nada demasiado serio con C# (sí programo, pero no es ni cerca mi lenguaje principal), pero es un lenguaje que me resulta sumamente interesante. Ya había hablado en este blog sobre las mejoras que trajeron las versiones 3.0 y 4.0.

En la última versión de C#, la 5.0 que salió con VisualStudio 2012, se agregaron dos nuevos comandos: async y await. De hecho, es de las pocas cosas nuevas que vinieron con esta versión.

Debo reconocer que al principio no le di mucha importancia, hasta que leí este artículo de Miguel de Icaza que me dejó pensando en el tema: Callbacks as our Generations' Go To Statement

¿Cómo funcionan los comandos async/await? (1)

Cuando se declara un método como async, lo que le estamos diciendo al compilador es que el método puede detener su ejecución en cualquier momento mediante el uso de un comando await.

A su vez, un método declarado como async tiene que devolver un objeto de tipo Task o Task<T> según su valor de retorno "real" sea void o T.

Cuando un método llama a otro que fue declarado como async, tiene la opción de llamarlo y esperar prefijando el llamado con el comando await, o puede seguir ejecutando cosas que son independientes, y guardarse la Task devuelta por el método para esperar más adelante.

Algo así:
public void TestAsyncCall()
{
    Task t1 = SomeLongTaskAsync();
    await AnotherLongTaskAsync();
    await t1;
    ShowCompletionMessage();
}
Por último, cuando un método queda a la espera de un comando await, pasa la ejecución al llamador para que pueda continuar con otra cosa, o quedar a su vez esperando en otro await.

En principio, visto así, no parece ser algo que nos vaya a cambiar la vida. Pero si uno lo piensa en relación al trabajo que puede dar resolver estos temas de forma manual, se da cuenta con la simpleza que lo lograron resolver y cuanto trabajo puede ahorrar.

Comparación con Objective-C

En Objective-C, y en particular para iOS desde la versión 4 en adelante, lo más parecido que tenemos es Grand Central Dispatch.

Supongamos que queremos tener una tarea que ejecute en background, y luego mostrar un mensaje de que terminó. Para eso, tenemos que escribir algo así:
dispatch_queue_t current_queue = dispatch_get_current_queue();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSError *error = [self someLongOperation];
    dispatch_async(current_queue, ^{
        [[UIAlertView alertForError:error] show];
    });
});
No es mala la solución, pero no tiene la simplicidad que tiene la de C#... Nótese que la mayor parte del código (en este caso, no siempre es así), es para decirle que ejecute de forma asincrónica. En C# lo escribiría tal vez así:
Error err = await SomeLongOperation;
ShowError(err);
Además puede tener algunos problemas.

Como estamos usando dispatch_async, si queremos esperar a que el llamado termine (como con el await), hay que hacerlo usando semáforos.

La alternativa podría ser usar dispatch_sync, pero en ese caso siempre estamos bloqueando, no tengo forma de seguir con ambas ejecuciones en paralelo. Además como el dispatch_sync puede usar el mismo thread de ejecución que el llamador, puedo potencialmente bloquear el thread de UI, cosa que no se debe hacer en ningún caso.

Conclusión

Me pareció una muy buena solución la de C#, creo que es bastante superior a las alternativas que se que tenemos en otros lenguajes.

También me resultó muy interesante el artículo de Miguel de Icaza. Si van al final del artículo hay un link a otro artículo que explica como otra persona implementó una especie de tutorial de su aplicación usando Mono y async/await. Vale la pena leerlo.

Me gustaría saber como se resuelve el tema de la ejecución asincrónica en otros lenguajes, así que agradezco cualquier comentario al respecto.

Actualización, 19/8/2013

Me corrige Fabián, y en parte tiene razón,  que GCD no es lo mejor para eso, que en Objective-C es más fácil usar NSOperationQueue.

Tiene razón en que es de más alto nivel que GCD, y facilita el tema de las dependiencias entre tareas (no se precisa usar semáforos), pero de todas formas se precisa bastante más código que en C# y la lógica queda partida en varios bloques, con saltos en la ejecución entre uno y otro.

En resumen, el argumento se mantiene: las construcciones async y await de C# facilitan mucho la tarea y hacen el código más fácil de leer.


(1) Es una interpretación libre de lo que pude leer e investigar... si me equivoco, agradezco me corrijan.

martes, 16 de julio de 2013

Consultar la base de datos offline en iOS

Hace unos días se liberó la versión Beta 1 de GeneXus Tilo, que tiene como una de sus características principales la posibilidad de generar aplicaciones Offline para Smart Devices.

Algo que puede resultar útil, sobre todo cuando estamos diagnosticando algún problema, es poder consultar directamente la base de datos.

En iOS no es algo evidente como hacer esto, por lo que lo dejo escrito acá por si alguien lo precisa. Esto sirve para el Simulator, no en un dispositivo...

Lo primero que debemos hacer es obtener la ruta al archivo SQLite. Para eso, debemos ejecutar la aplicación desde Xcode en el Simualtor. Debemos ejecutarla desde Xcode y no desde GeneXus, para así tener la opción de hacer debug.

Ponemos un breakpoint en algún punto por el que sepamos que va a pasar la aplicación (puede ser un procedure o el data provider asociado a un panel), y cuando se detiene, en la consola, escribimos:
po [[GXDBManager sharedInstance] databasePath]
Ese comando nos va a dar la ruta donde está el archivo SQLite con la base de datos, va a ser algo de la forma:
$0 = 0x0c871630 /Users/mcrispino/Library/Application Support/iPhone Simulator/6.1/Applications/1030E975-21FE-4C3F-8D4C-4836D8C34CEF/Documents/CM_Ios.sqlite
pero con otra ruta evidentemente, ya que depende de la aplicación.

Copiamos la ruta completa, desde el "/Users" hasta el ".sqlite" inclusive, abrimos la aplicación Terminal.app, y ahí escribimos
sqlite3 "<ruta_completa>"
donde <ruta_completa> es la ruta copiada en el punto anterior. Las comillas son importantes, porque la ruta puede tener espacios en blanco.

Una vez hecho esto, vamos a estar en la consola de SQLite, en la base de datos de nuestra aplicación. Ahí podemos hacer consultas SQL como lo haríamos normalmente en cualquier otro DBMS (respetando la sintaxis de SQLite, por supuesto). Por ejemplo:
select * from Products;
Para terminar, un par de comandos útiles:
.tables, lista las tablas que hay en la base de datos
.exit, sale de la consola de SQLite :)

lunes, 17 de junio de 2013

Metro México DF 2.0, primer aplicación Offline con iOS

Hace unos días, se liberó la versión 2.0 de la aplicación del Metro de México DF para iOS, hecha con GeneXus Tilo, y que tiene como novedad que incorpora la funcionalidad de generación Offline de esta versión. Hasta donde sé(1), esta es la primera aplicación Offline en iOS con GeneXus.

La generación Offline para aplicaciones de Smart Devices es una de las características principales de la versión Tilo, de la cual estamos por liberar una beta que agrega esta funcionalidad para iOS.

Por "aplicaciones Offline" en este caso, entendemos aplicaciones que tienen una base de datos en el dispositivo (SQLite, tanto en iOS como en Android), y tienen generación de código del lado del cliente, por lo que pueden funcionar completamente desconectadas del servidor.

En la versión Evolution 2 lo más parecido, es el caché de datos, que permitía acceder a los mismos a pesar de no tener conexión, pero no permitía ejecutar código del lado del cliente ni realizar modificaciones en la base de datos.

En particular la aplicación del Metro DF usa varias características del nuevo generador.

Para empezar, tiene todos los datos embebido en el SQLite, por lo que los accesos son mucho más rápidos.

Además se hace uso del generador de Objective-C, por lo que todos los cálculos se realizan locales, no hay ninguna llamada al servidor por ejemplo para calcular las rutas entre dos estaciones dadas.

También se realizan modificaciones a la base de datos mediante procedimientos, para cálculos internos, aunque esto no es visible para el usuario final ya que no hay ningún formulario de entrada de datos.

Otra cosa para destacar es que en la aplicación se usó la sincronización automática de datos (del servidor al cliente), para la carga inicial de datos en el SQLite, que luego se agregaron a la aplicación compilada (para evitar que cada instalación tenga que sincronizar).

Como decía, en breve vamos a tener una beta liberada para que puedan probar esta funcionalidad. No es una versión completa, porque quedan cosas por hacer, pero por lo menos tenemos la certeza de que ya hay un conjunto de aplicaciones que se pueden hacer. Y para muestra, ya pueden bajar la aplicación del Metro de México DF(2)


(1) Dado que el generador no está liberado, creo estar bastante seguro de que no hay otra aplicación liberada para iOS que use base de datos Offline. Si me equivoco, me avisan :)
(2) La aplicación tiene algunos puntos a mejorar, por lo que se va a estar subiendo una nueva versión, pero igual me pareció destacable que la aplicación ya esté en producción...

jueves, 13 de junio de 2013

Literales de tipo Date y DateTime en GeneXus

Cuando programamos, ya sea en GeneXus o en cualquier otro lenguaje, es muy común usar literales en el código, para valores conocidos o especiales de la aplicación.

Por ejemplo, si voy a hacer una suma de varios registros, inicializo la variable en cero:
&suma = 0
O si estoy por ejemplo en una transacción y quiero saber si estoy en modo Insert, puedo preguntar
if &Mode = 'INS'
    ...
endif
(nota: es un ejemplo... lo recomendable en este caso es usar el dominio enumerado TrnMode)

En GeneXus hay unos cuantos tipos de datos, pero podríamos decir que los principales son cuatro: números, strings, booleanos y fechas (o fecha-hora para ser más precisos).

Para los tres primeros, es bien sabido como expresar literales, ¿pero para las fechas?

Esta funcionalidad creo que no es muy conocida, pero en GeneXus también se pueden escribir literales de fechas, utilizando el caracter # como delimitador (así como usamos las comillas simples o dobles para los strings).

Así por ejemplo, las siguientes expresiones son válidas:
&date = #2013-06-13#
&dateTime = #2013-06-13 06:23p#
Por más información, pueden ver la documentación oficial en el Wiki.

Esto en sí no parece tener demasiadas ventajas con respecto a usar las funciones YMDtoD y YMDHMStoT, pero hay un caso donde sí en necesario usarlos: cuando queremos hacer un call dinámico a un objeto SD, que recibe una fecha por parámetro. Por ejemplo:
&callObject = "sd:MiObjetoSD?#2013-06-13#"
call(&callObject)
En todo caso, es una herramienta más de la que disponemos a la hora de escribir nuestras aplicaciones en GeneXus.

martes, 11 de junio de 2013

iOS 7

En el día de ayer Apple presentó la nueva versión de iOS, que ya se puede obtener en versión beta y estará disponible más adelante este año.

La presentación se puede ver completa en el sitio de Apple, donde tambiéan anunciaron una nueva versión de OS X (con poca cosa nueva...), nuevas MacBook Air y una nueva Mac Pro. Si quieren ver el video de presentación de iOS 7 específicamente (no la presentación), también lo pueden hacer.

Si bien se esperaba que el nuevo diseño en iOS 7 fuera más "flat" que en versiones anteriores, creo que con lo que mostraron se pasaron para el otro lado...

Todavía no instalé la beta, y dudo que lo vaya a hacer. Eventualmente voy a terminar actualizando, pero el diseño de esta nueva versión me parece mucho más descuidado que en iOS 6.

Por ejemplo, la aplicación del WWDC que liberaron hace unos días para el evento de desarrolladores, tiene "tabs" en la barra de navegación que se ven así:


En contraste, por ejemplo la aplicación de Calendario en iOS 6, tiene botones similares en la "toolbar", que se ven así:


Entre estos dos diseños, ya se nota la diferencia. En la aplicación del WWDC se le saca el aspecto redondeado a los botones y a la barra misma.

Sin embargo, a pesar que podía ser esperable algo similar para iOS 7, hicieron un cambio mucho más grande.

En iOS 7, ese mismo componente de UI se ve así:


En temas de diseño, evidentemente es una cuestión de gustos... pero el estilo "flat" que eligieron me resulta bastante malo.

En fin... Ya veremos el próximo año cuando anuncien el porcentaje de adopción de iOS 7, a ver si se acerca al que tiene iOS 6 hoy en día (que es del 93% de acuerdo a la presentación de ayer).


jueves, 16 de mayo de 2013

Realidad Aumentada en aplicaciones iOS con GeneXus

Hace un tiempo, Gastón hablaba sobre las ventajas del "Model Driven Development", y una de las que menciona es la extensibilidad.

Hoy sin duda esto queda demostrado, gracias a aplicaciones como ArTur MVD.

ArTur MVD es una guía turística de Montevideo para iOS y Android, desarrollada por la gente de DevXtend. Fue premiada durante el XXI Encuentro GeneXus (año 2011), y en su momento incluía una función de reconocimiento de monumentos, para lo cual alcanzaba con tomar una foto del monumento, y la aplicación nos mostraba la información del mismo.

Hace unos días subieron una nueva versión al App Store, que incluye la funcionalidad de realidad aumentada. Cuando uno apunta la cámara del dispositivo en una determinada dirección, muestra los restaurantes, comercios, hoteles, etc. que hay en esa dirección, dentro de un radio de 5 kilómetros.


La aplicación está hecha con GeneXus X Evolution 2, y la funcionalidad de realidad aumentada está provista por un user control desarrollado por la misma gente de DevXtend, que usa la información de "geolocation" de los items de un grid, la posición actual y la orientación del dispositivo, para saber que información debe mostrar.

El control por ahora lo tienen solamente para iOS, pero están trabajando también en una versión para Android. Además, estará disponible en el Marketplace para que lo pueda utilizar cualquiera (aclaro que desconozco como será el licenciamiento, si será pago o no...).

En la página correspondiente del Showcase pueden ver un video del control en acción.

lunes, 22 de abril de 2013

Mi primer media maratón

En el día de ayer corrí por primera vez una media maratón (21 kilómetros). Capaz que esta mal que yo lo diga, pero fue todo un logro :)

No hace mucho que empecé a correr, menos de tres años, y la verdad si en ese momento me hubieran dicho que iba a poder correr esa distancia, no les hubiera creído.

Hoy creo que con el entrenamiento adecuado, cualquiera puede correr la distancia que se proponga (salvo por algún impedimento médico, obviamente). Ayer por ejemplo, la edición de 10km de la carrera, la corrió un hombre con dos muletas...

El entrenamiento

Desde antes de empezar a prepararme para la media maratón, ya estaba usando una aplicación que se llama RunKeeper, que además de permitir registrar las corridas, tiene la opción de seguir programas de entrenamiento.

El que elegí yo era para correr la media maratón en menos de 2 horas y 15 minutos. Implicaba salir a correr entre cuatro y cinco veces por semana, con distintas distancias, velocidades, y con sesiones con cambio de ritmo. Fueron algo más de tres meses de entrenamiento y casi 500 km recorridos, que sin duda valieron la pena.

La carrera

La carrera fue ayer, como decía, a las 9 de la mañana.

Hubo bastante gente. No la cantidad que hay en las carreras masivas de 10km, pero me sorprendió tanta gente para correr los 21km. Después viendo los resultados, llegaron a la meta más de 800 personas.

El día estuvo muy lindo. Demasiado lindo capaz, cerca de las 11 de la mañana se hizo sentir el calor...

Pude cumplir mi objetivo de terminar la carrera primero, y luego de hacerla en menos de 2:15' ya que puse un tiempo de 2:09'30".

De todas formas venía mejor hasta el km 18, y los últimos tres fueron bastante difíciles. Así que en parte quedé con esa sensación de derrota, como cuando te empatan el partido en la hora.

¿Y ahora qué?

Por lo pronto ahora a descansar unos días, para darle un respiro a las rodillas.

En la primavera correré alguna de las carreras de 10km, a ver si puedo bajar de los 55 minutos, que era el objetivo para el 2012 y no logré cumplir.

Y después veremos... No creo que estos hayan sido los últimos 21km ;)

jueves, 11 de abril de 2013

Cosas que me gustaría tener en Objective-C

En los últimos tiempos, el lenguaje Objective-C ha mejorado bastante, como ya hemos comentado en el blog (por ejemplo acá).

Pero (siempre hay un "pero"), igual hay algunas cosas que todavía se podrían mejorar o agregar al lenguaje.


Implementación default para métodos de protocolos

Supongamos que tenemos un @protocol MyProtocol, que define una @property NSArray *someArray.

Cuando uso una variable que está basada en este protocolo (id<MyProtocol> myVar), puedo ver cuantos elementos tiene el array escribiendo
[[myVar someArray] count]
Pero supongamos que esto lo hago muy seguido, entonces quiero cambiar esto, para que el protocolo ya defina la propiedad elementCount para poder usarla como
[myVar elementCount]
¿Cuál es el problema con esto? Que si agrego la propiedad elementCount al protocolo, tengo que implementarla en todas las clases que implementan el protocolo.

Una alternativa sería tener algo así como "method templates", donde se pudiera definir la implementación del método en el protocolo mismo. Estos templates solo podrían usar otros métodos y propiedades definidos en el protocolo.

Templates o módulos

Yendo en la misma línea, hay casos en que una implementación de un método es genérica, sin importar la clase en la que esté.

El ejemplo más claro de esto es con los "singleton". Una posible implementación es la siguiente:
+ (instancetype)sharedInstance {
  static id _sharedInstance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
      _sharedInstance = [[self alloc] init];
  });
  return _sharedInstance;
}
(tomada de NSHipster, un excelente blog sobre Objective-C, ya que estamos)

Nótese que en esta implementación no se hace nunca referencia a la clase donde está definido el método. Lo único que se requiere es que tenga un método init.

Si tuviera algo equivalente a los módulos de Ruby (cuando se usan como "mixins"), podría simplemente "declarar" que una clase es un singleton, y que eso incluya la implementación del método sharedInstance.

Generics

En las APIs de Objective-C, cuando un método espera recibir un objeto genérico, declara que recibe un id, que indica que puede recibir cualquier tipo de objeto.

Esto es muy útil, y por ejemplo los arrays no controlan el tipo de objeto, por lo que podemos tener por ejemplo un número y un texto en el mismo array.

Sin embargo, hay veces que es útil saber de que tipo son los elementos de una colección, y forzar a que no se agreguen elementos de otro tipo.

Supongamos el caso que tenemos un protocolo que define una propiedad
@property (nonatomic, strong) NSArray *actions;
¿De qué tipo pueden ser las actions? No lo sabemos, a no ser que quien escribió el protocolo haya puesto un comentario... El lenguaje me debería ayudar a que esto quede más claro.

Boxing (y unboxing) de tipos básicos

En Objective-C, los arrays y los diccionarios solo pueden contener objetos, por lo que si queremos guardar por ejemplo un int en un array, tenemos que hacer el "boxing"y pasarlo al array como un NSNumber *.

Si bien no es algo complicado de hacer, por ejemplo;
int someInteger = 1;
NSArray *myArray = [NSArray arrayWithObject: @(someInteger) ];
podría ser el compilador el que se encargue de hacerlo...

Map & Reduce

Las funciones que tiene la API para manipular colecciones, en ciertos aspectos son bastante limitados.

Por ejemplo, no tengo funciones para Map y Reduce, que son muy útiles. Son fáciles de implementar, pero ya podrían venir incluidas.

Otras, por ejemplo para filtrar un array, son demasiado complicadas:
[[someArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^(id evaluatedObject, NSDictionary *bindings) {
  /* filtering code goes here */
}];
Debería poder escribir simplemente
[someArray filterWithBlock:^(id evaluatedObject) {
  /* filtering code goes here */
}];

Conclusión

Por suerte Objective-C es un lenguaje en evolución, así que tal vez podamos contar con alguna de estas cosas en el futuro...

sábado, 6 de abril de 2013

Comandos útiles de Google Search

Hace ya bastante tiempo que la página de búsqueda de Google dejó de ser un simple buscador de páginas web, y ha pasado a ser más una especie de aplicación de línea de comandos, que brinda información de contexto y no solo una lista de resultados.

Lo que sigue son algunos comandos útiles que se pueden usar en Google Search. No pretende ser una lista exhaustiva, son los que conosco y uso con cierta frecuencia.

Calculadora

Si en vez de ingresar un texto a buscar, ingresamos una operación aritmética como por ejemplo "2+3", Google nos devuelve el resultado, y muestra una calculadora como para poder seguir haciendo operaciones.

También permite usar funciones trigonométricas, logaritmos, exponenciales, factoriales, etc. Es bastante completa.

Gráfica de funciones

Si uno escribe una función, por ejemplo "x^2+1", el resultado que obtiene es la gráfica de la misma.

No es que use mucho esta funcionalidad, pero me hubiera servido en otro momento :)

Conversión de monedas

Si queremos saber algún tipo de cambio, digamos para saber cuantos dólares son 100 euros, escribimos "convert 100 euros to dollars".

Funciona también para pesos uruguayos, la única dificultad es que hay que escribir el comando en inglés: convert 100 uruguayan pesos to dollars.

Estado del tiempo

También podemos consultar el estado del tiempo en algún lugar, por ejemplo preguntando "weather montevideo", o simplemente "weather" y (a veces) Google se da cuenta en donde estamos.

El resultado muestra pronóstico por hora de la temperatura, lluvia y viento, y el pronóstico para los próximos días. Bastante completo, aunque no me puse a sacar estadísticas para ver que tan acertado es...

Información sobre películas, libros y música

Si buscamos por el título de una película, serie, libro, disco, o por autor, actor, director, etc., además de la clásica lista de resultados, muestra a la derecha de la página información sobre lo que estamos buscando.

Conclusión

La búsqueda de páginas web a veces es útil, pero puede ser una pérdida de tiempo si queremos encontrar algo específico. Google va por el camino de tratar de mostrar la información más relevante dada la búsqueda que estamos haciendo.

La lista de comandos que mencioné en el artículo puede resultar útil, seguramente haya otras cosas que no conozco, y seguramente se vayan agregando más comandos con el paso del tiempo.

¿Alguien sabe de otros comandos que se puedan usar en Google Search? ¿Algún otro buscador que haga este tipo de cosas?

jueves, 14 de marzo de 2013

Optimizaciones del compilador en Objective-C

Cuando se tiene un objeto que es inmutable, por ejemplo un string, es bastante útil contar con optimizaciones del compilador, para que no cree varias instancias idénticas.

Por ejemplo, en Objective-C si hacemos
NSString *a = @"hola";
NSString *b = @"hola";
el compilador se da cuenta que son referencias a la misma instancia, y crea el string @"hola" una sola vez en memoria, por lo que a y b quedan apuntando al mismo objeto.

Lo mismo pasa si se usan objetos de tipo NSNumber. Si tengo por ejemplo
NSNumber *c = @1;
NSNumber *d = @1;
el objeto @1 queda una sola vez en memoria y las variables c y d referencian la misma instancia.

Hasta acá no hay mucha sorpresa. Son objetos inmutables, por lo que no importa mucho que sean la misma instancia... salvo en lo que se puede ahorrar de memoria.

Una curiosidad si se quiere, es que los constructores no siempre pueden ser optimizados. Por ejemplo
NSString *e = [@"h" stringByAppendingString:@"ola"];
crea una nueva instancia del string @"hola".

¿Qué pasa con el método alloc? En principio uno podría pensar que si se usa alloc, debería devolver una nueva instancia. Esto sin embargo no es así para la clase NSNumber. El siguiente código devuelve la misma instancia que la asignada en c y d.
NSNumber *f = [[NSNumber alloc] initWithInt:1];
Raro...

Pero más raro aún, es que el método copy también devuelve la misma instancia.
NSNumber *g = [f copy];
En este caso, ¡c, d, f y g quedan apuntando todas a la misma dirección de memoria!

Esto no es necesariamente un bug o una feature, creo que se puede argumentar a favor y en contra de cada una de las dos opciones. Lo que sí es importante, es conocer como se comportan las herramientas que usamos.

Una última curiosidad,
[NSObject new]
siempre devuelve una nueva instancia, a pesar de que dos NSObject creados de esta forma son indistiguibles...

viernes, 22 de febrero de 2013

Leyes inconstitucionales

Capaz que es un poco ingenuo de mi parte, pero ¿el parlamento no debería asegurarse que las leyes que vota sean constitucionales?

En los últimos días hemos tenido por lo menos dos casos, con el ICIR y la ley interpretativa de la ley de caducidad, que la Suprema Corte de Justicia declara como inconstitucionales leyes que fueron votadas y promulgadas.

Es decir, tenemos un Parlamento con 99 diputados y 30 senadores (o por ahí...), que a su vez tienen secretarios y asistentes varios. ¿No deberían al menos tratar de que las leyes sean constitucionales? ¿Qué sentido tiene que voten leyes que no son válidas?

¿Son tan ignorantes que ni siquiera conocen la constitución? Parece como que debería ser requisito previo para ser diputado o senador, ¿no? Aunque en la realidad esto claramente no aplica...

viernes, 15 de febrero de 2013

Smart App Banners en Mobile Safari

Esta no la sabía, me pareció interesante como para compartirlo.

Aparentemente desde iOS 6, Safari tiene una funcionalidad llamada Smart App Banners, que permite que la página web muestre un banner con un link para bajar o abrir una determinada aplicación.

Del anuncio de Apple:
With Safari’s new Smart App Banner feature in iOS 6, you can display a banner that provides a direct link to your app on the App Store, or opens the app if the user already has it installed. Smart App Banners integrate seamlessly, have the same look and feel users know from the App Store, and are easy to implement.
Agregarlo en una página web cualquiera es muy fácil. Alcanza con agregar un meta-tag en el HTML:
<meta name="apple-itunes-app" content="app-id=529888450">
En ejecución se ve así:


Agregar esto en GeneXus también es muy fácil, alcanza con poner lo siguiente en el evento Start del Web Panel:
Form.Meta.AddItem("apple-itunes-app", "app-id=529888450")
Y por favor, si lo usan, úsenlo bien... No hagan como Quora que bloquea el contenido de la página web para que haya que bajar la aplicación.

lunes, 11 de febrero de 2013

Objective-C: ejecución sincrónica mediante semáforos

En Objective-C, o para ser más precisos en iOS, la norma es que cuando una tarea lleva tiempo, la misma se ejecuta de forma asincrónica mediante el uso de delegates y call-backs.

Por ejemplo, si queremos obtener el contenido de una página web, usamos NSURLConnection. El método start de dicha clase, inicia la operación y termina inmediatamente, antes de comenzar a recibir datos. Los datos llegan a través de los métodos del protocolo NSURLConnectionDelegate, como por ejemplo, connection:didReceiveData:.

Esto presenta un problema para la implementación de las clases estándar de GeneXus, ya que por ejemplo cuando hacemos
&httpClient.execute('GET', 'http://www.google.com')
&status = &httpClient.statusCode
esperamos que al volver de la llamada la variable &status tenga la información correspondiente.

La solución que encontramos para que la ejecución se realice de forma sincrónica, es mediante el uso de semáforos.

Los semáforos son parte de una tecnología llamada Grand Central Dispatch, que está disponible para iOS a partir de la versión 4.0.

La forma de implementar esto es la siguiente:
  1. cuando se llama al método que queremos que ejecute de forma sincrónica, se crea un nuevo semáforo mediante una llamada a la función dispatch_semaphore_create:
    _loadingSemaphore = dispatch_semaphore_create(0);
  2. la operación asincrónica se debe iniciar en otro thread (en particular hay operaciones que necesitan ejecutarse en el main thread), de forma que no haya bloqueos. En el ejemplo del HTTPClient, sería así:
    dispatch_sync(dispatch_get_main_queue(), ^{
      _conn = [NSURLConnection connectionWithRequest:request delegate:self];
      [_conn start];
    });
  3. cuando termina la operación asincrónica, se libera el semáforo de forma que los demás métodos puedan ejecutar:
    dispatch_semaphore_signal(_loadingSemaphore);
  4. mientras tanto, los demás métodos que necesitan que haya terminado la operación para poder ejecutar, como por ejemplo el statusCode, se bloquean hasta que termine la ejecución, luego realizan la tarea que necesitan y por último mandan un nuevo signal al semáforo por si hay alguna otra tarea esperando:
    dispatch_semaphore_wait(_loadingSemaphore, DISPATCH_TIME_FOREVER);
    block();
    dispatch_semaphore_signal(_loadingSemaphore);
Esta forma de implementar la ejecución sincrónica tiene además una ventaja: solo espera cuando es necesario. En el ejemplo, ejecutábamos el método execute e inmediatamente le pedíamos el statusCode, por lo que va a quedar esperando. Pero si antes de pedir el statusCode hacemos otra cosa, como por ejemplo ejecutar un procedimiento, puede ser que al terminar el procedimiento ya hayan venido los datos, y por lo tanto el statusCode no espera.

lunes, 28 de enero de 2013

Maps With Me: mapas sin conexión para dispositivos móviles

Hace poco estuve de viaje, y por lo tanto sin conexión a internet en el celular. Algo que hasta hace poco era la norma, usar mapas en papel, ahora me resulta bastante incómodo: es difícil ubicarse, en general se precisa más de un mapa (ciudad y rutas), es más complicado de manipuar, etc.

Es el problema de depender de la tecnología: uno se acustrumbra demasiado fácil :)

Por suerte para iOS (y también Android) existe una aplicación que tiene mapas de todos los países que se bajan y quedan locales en el teléfono, sin nececidad de conectarse a internet (solo se precisa conectarse una vez para bajarlos). Además como el GPS funciona aunque no tenga conexión a la red celular, tengo toda la funcionalidad que necesito.

La aplicación se llama Maps With Me, y si bien tiene una versión paga (cuesta U$S4.99), la versión grátis es bastante completa y se pueden usar los mapas sin restricciones. La versión paga agrega funcionalidad que puede resultar útil pero no es imprescindible: búsquedas y marcadores son las que me parecieron más necesarias.

Los mapas por lo que vi están basados en OpenStreetMap y son muy completos. Tiene mapas de ciudades y también de rutas, en algunos casos como por ejemplo en Montevideo, hasta con los números de puerta.

jueves, 3 de enero de 2013

Construir tus propios "Google Glasses"

No, no es que en el 2013 me voy a dedicar al hardware, ni les voy a enseñar como hacer sus propios "Google Glasses", pero vi esta nota en Lifehacker que me pareció interesante:
Build Your Own Google Glass-Style Wearable Computer
El autor del artículo original (al que refiere la nota en Lifehacker) cuenta como se hizo unos lentes sobre los que proyecta una pantalla, con partes que compró en eBay.

Lo interesante del caso es que si bien Google parece ser el único que está desarrollando de forma seria esta tecnología, si alguien lo puede hacer por sus propios medios, puede ser que aparezca alguna empresa nueva (startup) que se dedique a fabricar estos tipos de aparatos.

No es una tarea fácil, porque además del hardware se necesita desarrollar el software que lo acompañe, pero Google no planea liberar el producto antes de 2014, y acceder a un prototipo (que se supone va a ser este año) cuesta 1.500 dólares.

Quien sabe, a lo mejor alguien se anima a competirle a Google. Sería interesante...