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.
Blog de Marcos Crispino
Sobre desarrollo de software, tecnología, y algunas otras cosas
jueves, 16 de mayo de 2013
Realidad Aumentada en aplicaciones iOS con GeneXus
Etiquetas:
genexus,
user controls
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 ;)
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.
Cuando uso una variable que está basada en este protocolo (id<MyProtocol> myVar), puedo ver cuantos elementos tiene el array escribiendo
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.
El ejemplo más claro de esto es con los "singleton". Una posible implementación es la siguiente:
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.
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
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 {(tomada de NSHipster, un excelente blog sobre Objective-C, ya que estamos)
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
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;podría ser el compilador el que se encargue de hacerlo...
NSArray *myArray = [NSArray arrayWithObject: @(someInteger) ];
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:
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) {Debería poder escribir simplemente
/* filtering code goes here */
}];
[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...
Etiquetas:
desarrollo,
objective-c,
reflexiones
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.
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
Lo mismo pasa si se usan objetos de tipo NSNumber. Si tengo por ejemplo
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
¿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.
Pero más raro aún, es que el método copy también devuelve la misma instancia.
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,
Por ejemplo, en Objective-C si hacemos
NSString *a = @"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.
NSString *b = @"hola";
Lo mismo pasa si se usan objetos de tipo NSNumber. Si tengo por ejemplo
NSNumber *c = @1;el objeto @1 queda una sola vez en memoria y las variables c y d referencian la misma instancia.
NSNumber *d = @1;
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...
Etiquetas:
curiosidades,
desarrollo,
objective-c,
útiles
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...
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...
Etiquetas:
reflexiones,
uruguay
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:
Agregar esto en GeneXus también es muy fácil, alcanza con poner lo siguiente en el evento Start del Web Panel:
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.
Suscribirse a:
Entradas (Atom)




