viernes, 11 de abril de 2014

Disparo de eventos desde un User Control GeneXus en iOS

Los User Controls en GeneXus permiten extender la funcionalidad provista por los generadores, para resolver escenarios de User Interface que no están resueltos en los controles estandar. Hay varios controles interesantes en el Marketplace, principalmente para Web pero también para Smart Devices.

Si bien estos User Controls se utilizan mayormente para cambiar el aspecto visual de las aplicaciones, puede haber casos donde sea necesario ejecutar código del usuario.

En iOS esto es muy fácil de hacer, si la implementación del control hereda de la clase GXControlBase o alguna de sus clases derivadas.

Hay dos forma de hacerlo: disparando una acción del panel o disparando un evento del control.

Disparar una acción del panel

Para disparar una acción definida en el panel donde se encuentra el User Control, alcanza con incluir el siguiente código:
[self fireActionWithName:actionName userInterfaceContext:nil withEntityData:nil];
donde self es la implementación del user control y actionName es el nombre del evento como se definió en GeneXus.

Para poder disparar esta acción, obviamente necesitamos conocer el nombre de la misma. Puede ser fijo (y estar documentado...) o se puede configurar en una propiedad.

Disparar un evento del control

Otra forma de hacerlo es definiendo un evento en el User Control.

Para esto, debemos indicar en la definición del control que define un evento. Esto se hace en el archivo .control, y la forma de hacerlo es muy simple:
<Events>
    <Event>NombreDelEvento</Event>
</Events>
Luego, en el código GeneXus, podemos escribir:
Event Control.NombreDelEvento
    ...
EndEvent
Para dispararlo desde el código Objective-C, también en este caso es una línea
[self fireControlEvent:@"NombreDelEvento" userInterfaceContext:nil withEntityData:nil];
Un ejemplo de esto último lo pueden ver en el control SD Paged Grid, que define un evento PageChanged.

Si bien las dos opciones son equivalentes en cuanto a funcionalidad, creo que es mejor la segunda porque queda más fácil de entender el código GeneXus y facilita al usuario la posibilidad de encontrar esta funcionalidad de nuestro control.


martes, 25 de marzo de 2014

Cómo hacer una aplicación para iOS en 45 minutos

Es verdad, lo reconozco, el título de la nota es un poco tendencioso(1)... Pero también es verdad lo que dice, que pude hacer una aplicación e instalarla en mi teléfono en 45 minutos, desde la idea hasta tenerla en ejecución.

La aplicación es muy simple, obviamente. Necesitaba tener algunas notas para una presentación que iba a ser sin PPTs, para no olvidarme de decir nada.

En realidad me hubiera servido cualquier aplicación de notas, pero se me ocurrió que podría ser divertido hacer la aplicación, y con GeneXus es realmente fácil, así que me animé.

La aplicación tiene dos entidades con sus respectivos "work with": Presentaciones y Notas. Las Presentaciónes tienen un Título, las Notas tienen un Titulo y una Descripción. Solo eso.

La otra pantalla que tiene la aplicación es el "modo presentación", donde se elige la presentación que voy a hacer y se muestran las notas en un Paged Grid, el título más grande y la descripción al pie. Para pasar de una nota a la siguiente, se hace un gesto de "swipe".


Lo otro que es interesante, es que la aplicación es 100% offline. De hecho, si la hubiese querido hacer online hubiera sido más difícil, porque tendría que haber usado seguridad o como mínimo agregar el Device Id a todas las tablas para saber a quién corresponde cada presentación.

No intenté pasarla a Android, pero en cuanto a UI es todo por defecto, así que debería funcionar bien sin mucho esfuerzo.

Si a alguien le interesa la puedo publicar en algún GeneXus Server...


(1)En realidad no se puede considerar una aplicación terminada, porque le faltan los iconos e imágenes, y todo el proceso de publicación en el Store, pero la aplicación está funcionando y cumple con su objetivo.

jueves, 20 de febrero de 2014

Operador condicional ternario en Objective-C

En la muchos lenguajes de programación (en los que provienen de C al menos...) existe un operador condicional ternario de la forma
condición ? expresión1 : expresión2
que evalúa la condición, y el resultado de la expresión completa será expresión1 o expresión2 según el la condición evalúe a true o false respectivamente.

En Objecive-C, la condición debe ser evaluable como una expresión booleana, por lo que se puede usar un objeto, y en ese caso el significado es el mismo que preguntar si el objeto es distinto de nil.

Por otro lado, Objective-C adopta una convención del compilador GCC (según esta respuesta en StackOverflow), donde la expresión1 es opciona, pudiendo escribir
condición ? : expresión2
y en ese caso, si la condición es verdadera, será además el resultado de la expresión completa.

¿Para qué sirve todo esto? Sirve por ejemplo para escribir una asignación cuando el valor asignado puede ser nil y en ese caso queremos asignar un valor por defecto. Algo así:
id obj = someValue ? : @"";
Creo que el código queda bastante más simple y entendible (una vez que conocemos como funciona el operador) que hacerlo con if:
id obj = someValue;
if (!obj) {
    obj = @"";
}
También tiene la ventaja que la expresión se evalúa una sola vez, lo cual es útil cuando la expresión puede ser compleja, y necesario cuando la expresión puede tener efectos secundarios.
id obj = [[some complicated] expresion] ? : @"";
Por último, al escribir solamente una vez la expresión que queremos evaluar, eliminamos código duplicado lo que, como todos sabemos, es fuente de errores.

jueves, 9 de enero de 2014

Finanzas.app

Como comentaba Rodrigo en su blog Neurona Financiera, el registro de nuestros gastos es fundamental si queremos tener controladas nuestras finanzas. Yo también pasé como él por varias etapas: una planilla Excel, un programa Visual Fox Pro hecho con GeneXus 9.0 que después migró a .Net, una aplicación web en Google App Engine.

Finanzas.app es la siguiente iteración en este ciclo, está desarrollada en GeneXus Tilo B3, y es una aplicación que funciona completamente desconectada.

El hecho de que sea Offline es el punto fuerte de la aplicación. Seguramente no se compara en funcionalidad con las alternativas (por ejemplo ZetaCuentas por nombrar otra hecha con GeneXus), pero me permite tener los datos locales sin que intervenga un tercero. No me considero extremadamente paranoico, pero cuando hablamos de dinero, prefiero no darle la información a otros...

Cómo obtener la aplicación

La aplicación la estoy desarrollando para uso personal, por lo que no me interesa distribuirla en formato binario. Esto implica que no está en mis planes publicarla en los Stores, ya que eso requiere de un trabajo que no me aporta valor en esta etapa.

Sin embargo, sí creo que puede resultar útil a otras personas, por lo que estoy dejando disponible la base de conocimiento en un GeneXus Server. La licencia que decidí usar es la de MIT como comentaba hace unos días en el blog, porque creo que es la menos restrictiva en cuanto a lo que se puede hacer con los fuentes.

La URL para obtenerla es: http://open.genexusserver.com/tilo/home.aspx?FinanzasOffline,0

La aplicación está funcionando en mi iPhone con iOS 7, no la probé en otras plataformas ni en otros dispositivos...

Modelo de datos

En cuanto al modelo de datos, es muy sencilla, consiste solamente de cuatro tablas: Movimientos, Monedas, Rubros y Tipos de Rubro.

La tabla principal es donde se registran los Movimientos, que están compuestos por una Fecha, un Origen y un Destino. Tanto el origen como el destino consisten de una Moneda, un Rubro y un Importe.

Así por ejemplo, un movimiento puede ser: Fecha: 9/1/2014, Origen: $100 efectivo, Destino: $100 almuerzo. El origen es de donde sale la plata, el destino es a donde va.

Sobre las Monedas no hay mucho para decir, tienen un nombre, un símbolo y una descrpción corta para usar en el ingreso rápido (ver más adelante la explicación).

Los Rubros tienen una descripción y un Tipo de Rubro, que es una forma de agruparlos. A su vez cada Tipo de Rubro puede ser un Ingreso, un Gasto o un Activo (es decir dinero; contadores abstenerse, seguramente el nombre no es el adecuado).

 

Ingreso rápido

En cuanto a funcionalidad tampoco hay mucho para decir. La característica principal es el "Ingreso rápido", que agiliza mucho el registro de movimientos. Esto es fundamental para que no nos resulte pesado el registro, para que no dejemos de usar la aplicación.

De hecho, el ingreso rápido es la primer pantalla que nos encontramos al abrir la aplicación. Allí vamos a ver simplemente dos campos: una fecha y una descripción.


La descripción debe tener el siguiente formato:
<moneda origen> <importe origen> <rubro origen>, <moneda destino> <importe destino> <rubro destino>
con las siguientes reglas:
  • la <moneda origen> se puede omitir, y en dicho caso se usa la moneda por defecto (que se configura en los parámetros de la aplicación)
  • el <rubro origen> se puede omitir, en cuyo caso se usa el rubro por defecto
  • la <moneda destino> se puede omitir, en cuyo caso se usa la <moneda origen>
  • el <importe destino> se puede omitir, en cuyo caso se usa el <importe origen>
  • el <rubro destino> se puede omitir, en cuyo caso se usa el rubro por defecto
  • para las monedas se usa la descripción corta que es un Character(3)
  • para los rubros se busca por LIKE, y se queda con el primero que encuentra
Esto permite que el ingreso sea muy sencillo. En el ejemplo que ponía antes, lo único que tengo que escribir es: "100, alm", suponiendo que el rubro por defecto es Efectivo y la moneda es Pesos.

Si quiero registrar un retiro de $1000 del banco, tengo que escribir simplemente "1000 banco"; y si quisiera que fuera en dólares (suponiendo que la descripción corta es "usd") debería escribir "usd 1000 banco".

Configuración inicial

Evidentemente la organización de nuestras finanzas es algo bastante personal, por lo que los rubros que cada uno defina serán los que le sirvan.

Podemos hacer la carga inicial de forma manual usando los "work with", pero esto puede ser bastante tediosos. Para facilitar la tarea, hay un data provider en la KB (llamado DatosInicialesRubros) que hace la carga inicial de Rubros y Tipos de Rubro, que cada uno deberá modificar de acuerdo a sus necesidades.

Consultas

La aplicación por el momento tiene solamente dos consultas: Resumen mensual y Saldos

El Resumen mensual muestra por Tipo de Rubro y Moneda, todos los movimientos que sean Ingreso o Gasto del mes. Se puede consultar para el mes actual y para el mes anterior.

La consulta de Saldos muestra por Rubro y Moneda para aquellos rubros que son de Activos, el saldo a la fecha especificada. La fecha puede ser hoy, el mes anterior o el año anterior.

Estado actual y futuro

Esta aplicación es la que estoy usando para el registro de gastos, desde el 1º de enero dejé de usar la aplicación que tenía antes. Por lo tanto, puedo decir que la aplicación está en producción y con el sello de calidad:


A futuro quedan sin dudas cosas a mejorar. Le falta mucho de estética, de lo que no me he preocupado por ahora, y también le falta en cuanto a funcionalidad.

Si alguien la prueba y quiere colaborar, puede pasarme los cambios que (si los apruebo) yo los publico en el GXServer. Si encuentran problemas y no los quieren arreglar... me avisan que los iré corrigiendo en la medida que tenga tiempo de hacerlo.

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...