Programador de Videojuegos

He realizado diversos trabajos como programador de videojuegos.

Mis clientes a veces me piden juegos en 2D/3D para diversas plataformas, me encargo del desarrollo del juego multiplataforma con Shiva 3D/Codea/C++/Objective C, etc.
Aquí se muestran algunos de los videojuegos producidos para la venta:

también he realizado diversos juegos freesource:

  • Abduction ( juego de rescatar personas de un planeta con diferentes toques con los dedos)
  • Sprint Animal ( scroller en horizontal de un bichito que salta)
  • Speed Shooter Earth ( shoot’em up de naves sobre los mapas de Google Maps en varios aeropuertos)
  • Towers versus Zombies ( como el Plant vs Zombies pero con torretas y más simple :) )
  • Conecta 4 (forma parte de una serie de juegos de mesa para tabletas)

en estos momentos me encuentro desarrollando otros juegos:

estos son los juegos de los concursos, como Ludum Dare:

 

He participado con empresas en la creación de componentes para videojuegos, no puedo poner los nombres por acuerdos.

 

Aplicación de iOS para Abades con gestión de datos en Google App Engine

Detalles de la aplicación para iOS

¿Para qué sirve?

  • Proporciona información de los establecimientos del Grupo Abades
  • Muestra las últimas noticias, promociones, eventos y tweets de las redes de la empresa
  • Se pueden conseguir puntos de Abades, con un número de puntos suficiente, el usuario puede canjear estos por regalos que cada establecimiento pone a disposición de clientes que los frecuentan.

 

Funcionalidades de la aplicación

1.- Información de los establecimientos de la empresa:

El proceso de funcionamiento de la aplicación del Grupo Abades es el siguiente: hay una caché de días en los que la base de datos no se actualiza a menos que el usuario lo requiera. La primera vez que se arranca este número de días y otros parámetros de configuración se ponen a 0, por lo que si entramos en el listado de establecimientos de un tipo: áreas de servicio, hoteles o restaurantes, se descargará de Google App Engine, los datos que se han guardado en el modelo de datos del servidor por los empleados de la empresa en formato XML y se insertarán en la base de datos local en alrededor de un minuto. A partir del momento en que se almacenan estos datos hay 6 dias en los que no se vuelven a actualizar al ser datos de un tipo distinto a las promociones/eventos, noticias o ficheros,  se mantienen actualizados con otra frecuencia distinta.  En el caso de ser una imagen, la primera vez que se descarga se guarda en una caché (* nota programadores: de ASIDownloadCache), que hemos visto en los cursos de programación de este blog, entonces se almacenarán durante el número de días configurado, 31 por defecto, ya que son más estables y no deberían cambiar a menudo. Veréis que hay mucha info que aparece en la aplicación que es replicada en la web, sin embargo hay secciones como los puntos de sitios de interés que es más completa en esta aplicación de iOS. Las subsecciones de esta funcionalidad son las  siguientes:

  1. Información:  es una réplica de la información de un establecimiento, dispone de un texto introductorio para colocar sobre el mapa, el título del establecimiento, su imagen en miniatura, una descripción ampliada junto con la información de los servicios que ofrece y las estrellas o rango establecido en función de lo que sea.
  2. Situación:  sólo abrir la pestaña de situación de un establecimiento encontraremos la posición GPS en el mapa de Google Maps para iPhone. De hecho, si pinchamos sobre la chincheta roja aparecerá un cartel indicando el nombre del establecimiento, su rango y su localización exacta de longitud y langitud en formato de coordenadas geodésicas de gmaps. El botón a la derecha con signo más (+) indica la posición actual del usuario y el de la flecha a la derecha (>) hace que el mapa se posicione donde se encuentra el establecimiento. Por último, sin pinchamos en el icono de información (i) aparece un menú para seleccionar un sitio de interés relacionado con la posición del establecimiento, al seleccionar uno de estos sitios, el mapa “volará” hasta su posición y colocará allí una chincheta violeta, esta, muestra un cartel con el nombre y un botón al pinchar sobre ella, este botón nos muestra un recuadro de información sobre el sitio de interés, que contiene un texto con enlaces que se abrirán en el navegador Safari para ampliar la información, calcular nuestra ruta,etc. El botón de la flecha nos llevará a la aplicación de Google Maps de iPhone para mostrarnos todos los establecimientos de la empresa y poder calcular rutas o realizar cualquier otra operación con dicha aplicación.
            
  3. Imágenes: al pinchar sobre la pestaña aparecería una galería de imágenes con sus respectivos títulos en la parte superior de la ventana, los botones anterior (|< ) y siguiente ( >| ) permiten navegar con transiciones por las distintas imágenes disponibles. El botón de Play ( |> ) hace que comience la reproducción de imágenes en secuencia, hasta que volvamos a pulsar sobre la imagen una vez. Si mantenemos el dedo presionado sobre la imagen unos segundos o pulsamos sobre el icono de compartir ( []=> ) aparecerá un menú que nos brinda la opción de poder guardar la imagen en nuestra galería de imágenes. Si se ejecuta una doble pulsación, agrandaremos la imagen y el gesto de peñizcarla además modificará su tamaño. Podemos arrastrar la imagen por la pantalla o girar el dispositivo para verla con una disposición distinta.
            
  4. Contacto:  esta sección de información de un establecimiento consiste en proporcionar al cliente toda la información referente a aquel, ya sea el teléfono, la web o el email con sus respectivos botones para acceder a estas funcionalidades, hasta la dirección, Fax y por último más información sobre el emplazamiento.
  5. Mediante el uso del botón refrescar en el listado de establecimientos podemos recargar manualmente los datos de la base de datos de estos.
  6. Las opciones de configuración de la aplicación para caché de imágenes, bases de datos, GPS, segundos entre cada pase de imágenes, y otras están en la aplicación de Ajustes de iOS -> Grupo Abades

2.- Promociones y eventos:

los empleados de la empresa, a través de la herramienta web de administración de promociones, colocan en abades.com y sus otras webs una lista de promociones, esta lista es accesible por medio de un fichero XML,tal como en la funcionalidad anterior se almacena una caché temporal de estas que permite visualizar una lista con títulos, centros y una imagen asociada, al pinchar sobre cualquiera de ellas entraríamos en la presentación de la imagen de la promoción ampliada, un texto descriptivo de la misma así como la información de contacto: teléfono, web, mail, etc. Su fecha y el centro de emplazamiento de la promoción o el evento.
     

3.- Mi cuenta en Abades:

Esta sección de la aplicación se utiliza para crear una cuenta con la que poder reunir puntos y reservar, con el fin de,  luego, canjear regalos, que ofrece la empresa a sus clientes, agradeciendo la estancia de estos en sus establecimientos. Lo primero que debemos hacer es entrar en Mi cuenta -> Crear Cuenta , rellenar los datos de: e-mail, contraseña y tu nombre y pulsar en el botón “Crear Cuenta“. Hecho esto nos llegará un e-mail de activación en el que debemos buscar un enlace para activar la cuenta en la aplicación de google app engine. Una vez activada nuestra cuenta, podemos rellenar los datos en “Mi cuenta -> Tengo Cuenta”  y pinchar en “Entrar con mi cuenta“, si todo ha ido bien aparecerá una ventana con los datos de tu cuenta, e-mail, nombre y puntos abades.
Estos puntos se consiguen por medio de checkins usando una cuenta de Foursquare. Es un requisito fundamental ya que todos los establecimientos se encuentran asociados a la cuenta de foursquare. Ahora pincharemos en el activador ON/OFF de Foursquare e introducimos nuestro usuario (email/teléfono) y password de esta red. Al pinchar aceptar debe aparecer un botón “Cerrar sesión“, lo cual nos indica que hemos iniciado la sesión correctamente, podemos usar el icono de cerrar (x) para volver a los datos de nuestra cuenta y ahora el indicador on/off estará activado, ya podemos hacer checkins desde el Mapa de la aplicación.

4.- Mapa:

Esta funcionalidad de la aplicación del Grupo Abades se utiliza para colocar todos los establecimientos de la empresa sobre el mapa y poder encontrar el que esté más próximo a nosotros con objetivo de realizar un checkin (estoy aquí) y recibir los puntos que proporciona el establecimiento por hacerlo cada día.
Hay que tener en cuenta que el cálculo de la distancia se realiza en línea recta por lo que si queremos saber la verdadera medida de distancia para viajar al establecimiento debemos usar otro software de GPS, para eso podemos utilizar el botón Google Maps para ver las posiciones de los establecimientos sobre la aplicación Google Maps de iPhone y calcular una ruta óptima en coche, bici, transporte público o a pie desde nuestra posición actual. Sólo cargar el mapa aparece una chincheta roja y si pulsamos sobre ella veremos que refleja nuestra posición GPS actual,

     

tal como reza el cartel superior derecho de Latitud y Longitud. En este mismo cartel podemos controlar el nivel de zoom del mapa con el deslizador dispuesto a tal efecto. Otros controles útiles son el centrador de posición ,con el botón “Mi posición“,  tendremos actualizada nuestra posición en todo momento, centrando el mapa en esta cada cierto número de segundos. Los botones Mapa|Sat|Híb modifican el formato de visualización de aquel de mapa a satélite o híbrido (mezcla de los dos primeros). Ahora, usaremos el botón con fondo azul y un radar blanco para que la aplicación busque el establecimiento más cercano a nuestra posición (si es que hemos permitido a esta el uso de nuestra localización,claro), y se mostraría una ventana con el título “El sitio más cercano es:” y el título y los kilómetros de distancia, al aceptar observaríamos que el mapa ahora está posicionado con el centro en dicho establecimiento y que pinchando sobre el icono de chincheta de Abades ([a]) aparece un recuadro con el título, categoría y un botón para mostrar una ventana que contiene información del sitio y un botón para hacer Checkin.

    
Al hacer checkin la aplicación intentará conectar con el servidor enviando los datos de nuestra cuenta y si hemos configurado que se muestren nuestros checkins en foursquare y redes sociales twitter y facebook aparecerá un mensaje del tipo “Chekin desde la app de Abades en el establecimiento…., obteniendo … puntos” con la información correspondiente, en estas redes sociales y en una ventana de información que nos confirma que todo ha ido correctamente, con nuestro nuevo saldo de puntos de Abades, ahora podemos consultar nuestra cuenta ,de la funcionalidad anterior y asegurarnos de que efectivamente tenemos esa cantidad de puntos de la que nos han informado. Lógicamente un usuario sólo puede hacer uso de una cuenta de Foursquare, en el momento en que la cuenta se utiliza para hacer un checkin en otra cuenta que no es la suya se borrarán todos los regalos reservados y puntos asociados.En el momento de hacer checkin además seremos informados de las últimas ofertas exclusivas para Foursquare y el Grupo Abades.

5.- Noticias y tweets:

este módulo consiste en la lectura del RSS generado por Yahoo! Pipes pasado a través de Feedburner para gestionar social media de todas las últimas noticias de todos los blogs y webs de la empresa, se muestra un listado de las últimas novedades y al pinchar en una de ellas aparece un resumen,  con título y fecha, podemos leer la noticia ampliada en la misma ventana mediante el icono del mundo o bien abrirla en el navegador Safari por medio del botón de “enviar a”  ([]=>). La sección de tweets accesible desde el logotipo de la empresa es exactamente igual en cuanto a funcionamiento pero sólo de la red Tweeter.
            

6.- Sección de regalos:

quizás la parte más interesante para los clientes, pues reciben algo físico a partir de su acción virtual. Consiste en reunir puntos de Abades por medio de checkins en los distintos establecimientos de la empresa, y ,tras consultar el listado donde aparece con un scroll horizontal, los diferentes premios
 
que podemos conseguir con estos puntos, seleccionamos el que deseemos y lo reservamos, después nos dirigimos al establecimiento donde se encuentra dicho item reservado y lo canjeamos en el punto habilitado para ello. En caso de cancelación se devolverían los puntos a la cuenta.

Para programadores:

El Reto

El reto de esta app consistió en poder crear una herramienta que sincronizara los datos de la web actual (abades.com) con los demás dominios (abadestriana.com, abadesnevadapalace.com) y appspot.com, y después crear un algoritmo para hacer un checkin válido así como sincronizar todos los elementos RSS en un único canal y adjuntarlo al programa de promociones actuales de las webs (blogs, noticias, etc. para cada idioma, inglés y español).

¿Qué usa la aplicación?

  • Un conjunto de servicios webs habilitados en una aplicación en Google App Engine para rellenar el contenido de la base de datos SQLite que trae (populate) instalada.
  • API de Foursquare para hacer checkin (con twitter y facebook habilitados)
  •  CoreLocation de iOS para comprobar la distancia al establecimiento más cercano, enviando si las condiciones se cumplen, un mensaje encriptado al servidor de appspot que los comprueba y aumenta los puntos de la cuenta de usuario que está utilizando la app.

¿Cómo se hizo?

La programación empezó donde véis el artículo de como crear una aplicación productiva y social, el esquema era el mismo, pero había que añadirle toda la parte de programación de servidor, ya que la empresa necesita tener actualizada la información que se comparte con los dispositivos y sus webs, la opción de crear un algoritmo en PHP era tentadora pero una aplicación de móbil necesita una buena respuesta y un servidor web no es precisamente una máquina en la que uno desee confiar plenamente, es decir, no siempre está operativa 100% porque tiene muchos usuarios desde la web, por lo que opté por un servidor de google app engine y usando Java creé todas las estructuras de datos donde almacenar la información de establecimientos como en las guías de los cursos de este blog, después lancé los servicios web que permiten dar de alta todos estos datos, y que son capturados por los analizadores sintácticos de la aplicación móvil, más adelante configuré efectos gráficos, mejoré el diseño y añadí la API de Foursquare gracias a frameworks ya disponibles en la red.

El analizador sintáctico es NSXMLparser, el framework ASIHttp permite descargar imágenes con caché de forma síncrona o asíncrona, el scroll view de regalos usa una caché para poder crear todos los que hagan falta, el modelo de datos es sencillo, contiene elementos transformables para almacenar listas, estas listas en Google App Engine con DataStore son persistentes y embebidas en clases que permiten la serialización de objetos, etc.

En cuanto a los mapas no hay mucho que contar, es lo que se suele hacer, una clase para las anotaciones, se captura el evento del dibujado para dibujar un icono distinto e intercalar un botón en recuadro que se muestra al pulsar sobre una chincheta o anotación, y las animaciones están hechas con QuartzCore, es decir UVAnimation con cambios entre transiciones de opacidad, tamaño y posición,…lo sé ,aún se puede mejorar :)

Puedes descargar la aplicación desde su sección: aquí.

Crear una aplicación productiva y social para iOs con interfaz web

En este artículo cuento mi experiencia de creación de la aplicación de iOs TimeBox ,desde el estudio de mercado, diseño, y elaboración de la interfaz gráfica  hasta la programación, creación de la base de datos y la aplicación web para redes sociales.

Lo primero que debemos hacer es tener claro qué tipo de aplicación queremos crear, para eso hay que tener una buena idea, y si la tenéis pero la aplicación ya está hecha, entonces comprar/bajaros todas las aplicaciones que han implementado vuestra idea y estudiarlas para encontrar en qué fallan, cómo las podéis mejorar y combinar, es decir, encontrad una motivación para programar, porque esto es duro amigos jeje (más…)

Geolocalización y multitarea en iOS: posicionamiento GPS y MapKit de Google

En este capítulo de programación del Curso de Aplicaciones de iOS me gustaría hablar sobre la localización en dispositivos que usan iOS así como de la multitarea ya que son cosas muy relacionadas, por eso remataremos el tema con los avisos, es decir, notificaciones locales, esos mensajitos que los programas lanzan, y como poner un número en nuestro icono de nuestra aplicación, también llamado “badge”; que por cierto también lo tiene la clase UITabBarItem, o sea, un botón de una barra de botones tipo pestañas o tab’s… ;-)

Multitarea en las aplicaciones de iOS

Las plantillas generadas por el IDE XCode 4 ya traen las funciones que toda aplicación debe utilizar cuando la aplicación pasa entre los diferentes estados que iOS le permite. Cuando usamos el botón Home desde nuestra aplicación hay un evento, otro cuando este ha terminado, otro cuando se vuelve a la aplicación, etc.

Debemos utilizar la menor cantidad de recursos posible durante la multitarea ya que si nos pasamos el SO decidirá cerrar nuestra app aunque la mayoría de apps no ejecutan ningún código durante su tiempo en modo de segundo plano.

Esta multitarea, como sabemos está disponible sólo para dispositivos con firmware a partir de la versión 4, que fué cuando se introdujo dicha característica, para ello existe la función del SDK multitaskingSupported que nos informará de cada caso.

 
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;

Lo primero que debemos hacer es incluir la opción UIBackgroundModes en nuestro Info.plist, y se debe especificar los valores: audio, location y/o voip para disponer de estas características durante el trabajo en segundo plano.

En nuestra clase Delegate de la aplicación tenemos las funciones siguientes:

  • applicationdidFinishLaunchingWithOptions
  • applicationDidBecomeActive
  • applicationWillResignActive
  • applicationDidEnterBackground
  • applicationWillEnterForeground
  • applicationWillTerminate

No se recomienda utilizar llamadas a OpenGL (tampoco sería muy lógico) durante el segundo plano, debemos cancelar todos los servicios Bonjour antes de pasar a este estado así como no mostrar mensajes si hay errores de conexión, guardar datos, etc. Si tenemos una aplicación que muestra muchos datos visuales lo mejor es liberarlos hasta que se vuelva del segundo plano, pero sí debemos responder a las notificaciones de conexión y desconexión.

Para realizar la inclusión de una tarea en segundo plano existe la función llamada beginBackgroundTaskWithExpirationHandler, que pide al sistema un tiempo extra para completar una tarea larga y para finalizarla tenemos endBackgroundTask, sabemos el tiempo que lleva ejecutándose gracias a la propiedad backgroundTimeRemaining de nuestra UIApplication. Deberíamos usar estas funciones y propiedades si estamos descargando archivos de datos, configurando o guardando información sensible.

Un ejemplo:

 
-(void)applicationDidEnterBackground:(UIApplication *)
application {
UIApplication* app = [UIApplication sharedApplication];
 
//Pedir permiso para ejecutar en background. Proveer de un manejador por
//si la tarea requiere más tiempo
 
NSAssert (bgTask == UIBackgroundTaskInvalid, nil);
 
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
dispactch_async(dispatch_get_main_queue(), ^{
 
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
})
}];
 
dispatch_async(dispatch_get_global_queue(
    DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
});
});
}

Notificaciones:

No son más que tareas en segundo plano pero que usan la clase UILocalNotification, los tipos existentes son: sound (sonido como los de whatsapp o un sms),alert (el texto de un sms, etc.)  y badge (el número que hay sobre el icono de la app).

Se pueden programar hasta 128 notificaciones simultáneas, cada una de ellas se puede repetir en un intervalo prefijado por el programador.

Veamos un ejemplo de una alarma con un archivo de sonido donde se comprueba si está en segundo plano:

 
- (void) scheduleAlarmForDate: (NSDate *) theDate {
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = 
 [app scheduledLocalNotifications];
 
if ([oldNotifications count] &gt; 0)
[app cancelAllLocalNotifications];
 
UILocalNotification* alarm = [[
  [UILocalNotification alloc] init] autorelease];
 
if (alarm) {
alarm.fireDate = theDate;
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = 0;
alarm.soundName = @"alarmsound.caf";
alarm.alertBody = @"Time to wake up!";
 
[app scheduleLocalNotification:alarm];
}
}

Para el audio se indica en nuestro Info.plist, una cadena “audio” en UIBackgroundModes como hemos visto, cuando la aplicación esté en segundo plano ,limitaremos que se ejecute más de lo necesario…En el caso de “voip” se debe configurar además un socket, por medio de la función setKeepAliveTimeout: handler: para especificar la frecuencia con la que despertar la aplicación para el correcto funcionamiento del servicio.

Para las notificaciones por posicionamiento, sólo se realizan con cambios significativos de localización, aunque pueden seguir usando los servicios de localización en segundo plano no conviene abusar a menos que desarrollemos un GPS como Tomtom.

Activación:

 
CLLocationManager *locManager = [[[CLLocationManager] alloc] init ];
[locManager startMonitoringSignificantLocationChanges];

Podemos además usar la característica de despertar y relanzar nuestra aplicación si nos llega una notificación push.

Location Framework

El posicionamiento es posible usarlo a través del SDK de Apple con un iDevice gracias al Location Framework, que nos proporciona la localización del usuario, información que podemos utilizar para mostrar recursos cercanos, orientar en rutas, realizar un seguimiento o tracking del desplazamiento, dibujar zonas o áreas sobre el mapa con líneas y otras primitivas geométricas, etc. Además el Location Framework da acceso a la brújula para mejorar la experiencia a la hora de realizar una orientación más realista, por ejemplo se usa en la aplicación Mapas de Google al pulsar el icono de brújula

Configurando el método de posicionamiento

iOS nos permite conseguir la localización actual con diferentes métodos de posicionamiento:

  • Por cambio significativo: es un método para bajo consumo de batería
  • Servicios de posicionamiento estándar: es más configurable
  • Monitorización por región: para registrar cambios en una zona definida

Para aplicaciones que utilicen CoreLocation.framework ( #import <CoreLocation/CoreLocation.h>) además debemos añadir al Info.plist (recordar cómo se configura una aplicación en la anterior entrega de este curso de apps) de nuestra aplicación el campo UIRequiredDeviceCapabilities y la llave “location-services” si sólo necesitamos posicionamiento general (aproximado, por ejemplo con una triangulación por antenas de telefonía bastaría) o bien especificamos además la llave “gps” para usar el hardware del iPhone y el iPad (1,2,etc).

Una vez seleccionado el método y configurada la aplicación, pasamos a inicializar el servicio de posicionamiento, para ello utilizamos la clase CLLocationManager,

Obtener la posición del usuario (por defecto)

Nada mejor que un ejemplo para mostrar el funcionamiento:

- (void)startStandardUpdates
{
// Crea el objeto location manager si no existe
if (nil == locationManager){
locationManager = [[CLLocationManager alloc] init];
    }
    //Especificamos la clase actual como clase que maneje los
    // eventos del localizador
locationManager.delegate = self; 
//la clase actual debe heredar de CLLocationManagerDelegate
    //Ahora especificamos una precisión
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
 
// para los movimientos entre eventos que suceden:
locationManager.distanceFilter = 500;
    //inicializar ya!
[locationManager startUpdatingLocation];
}

Con desiredAccuracy (kCLLocationAccuracyBest) se establece la precisión del posicionamiento, con distanceFilter (kClDistanceFilterNode) se configura la distancia mínima que es necesario que se aprecie en un desplazamiento del dispositivo para que se produzca un evento de actualización de la localización actual del usuario.

Obtener la posición del usuario por cambio significarivo

- (void)startSignificantChangeUpdates
{
// Crear el objeto the location manager si no existe:
if (nil == locationManager){
locationManager = [[CLLocationManager alloc] init];
    }
locationManager.delegate = self; //igual que antes
    //esto es lo nuevo, inicialización con actualización por cambio significativo
[locationManager startMonitoring SignificantLocationChanges];
}

Crear una clase para las “chinchetas” o pins sobre el mapa, esta clase almacena información relevante sobre lo que queremos mostrar al pinchar sobre ella, desde el propio pin o chincheta (situación, color, imagen, animación,etc.) además del pequeño título y subtítulo que se muestra y la posterior acción a realizar al extender la información de la misma.

¿Qué es un “pin”?: Podemos extender la clase (por herencia) de una MkAnnotation y luego en la clase controlador de la vista (UIViewController) extender esta al delegado de un mapa (UIVIewController <MkMapViewDelegate>) para poder recoger el evento de cuando se pinta un pin o chincheta y decirle al objeto MkMapView cómo mostrar la información que almacena la clase anotación, es en este evento controlado donde cambiamos la imagen del pin asociado al MkAnnotation, el botón de acción o cualquier otra cosa que queramos a voluntad.

Como observación, recordad que cuando MKMapView carga podemos utilizar la posición del usuario que viene integrada como opción (UseUserLocation, el framework CoreLocation nos proporciona otra, debemos saber cuál utilizar en qué caso ya que no podemos tener siempre cargado un mapa, o sincronizar los valores de diferentes clases cada vez que la posición cambia, etc. Si ocurre un error al recibir la información de localización el evento locationManagerdidFailWithError nos informará de ello, a veces, el gestor de posicionamiento devuelve información en una caché así primero debemos comprobar en qué momento se generó mediante la propiedad timestamp de las posiciones recibidas.

Para consumir menos batería debemos desactivar los servicios de posicionamiento por medio de una configuración de usuario o bien cuando no se necesiten; usar el servicio de cambios significativos en lugar del servicio estándar, y un valor pequeño para distanceFilter (igual que en los videojuegos ,mientras mayor es el búfer, más memoria y procesamiento se necesita por lo que consume más batería). Por último, desactivar los eventos de posicionamiento si la precisión (diferencia entre cambios de posición) no mejora en un corto período de tiempo.

Información geolocalizada

Lo interesante de poder disponer de un framework de Google MapKit es que podemos extraer información a partir del campo longitud y latitud, un ejemplo que he utilizado en una de mis aplicaciones es el siguiente:

- (void) searchBarTextDidEndEditing:(UISearchBar *) _searchBar {
    [searchBar resignFirstResponder];
    NSError *error;
    NSString *urlString = [NSString stringWithFormat:
@"http://maps.google.es/maps/geo?q=%@&output=csv",
[_searchBar.text 
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    NSString *locationString = 
[NSString stringWithContentsOfURL:
 [NSURL URLWithString:urlString] 
encoding:NSISOLatin2StringEncoding 
error:&error];
    NSArray *listItems = [locationString 
componentsSeparatedByString:@","];
 
    if ([listItems count]&gt;=4 && 
[[listItems objectAtIndex:0] isEqualToString:@"200"]){
        if (birthLocation!=nil){
         [mk_mapView removeAnnotation:birthLocation];
         [birthLocation moveAnnotation:CLLocationCoordinate2DMake(
          [[listItems objectAtIndex:2] doubleValue], 
[[listItems objectAtIndex:3] doubleValue])];
            [birthLocation setTitle:_searchBar.text];
            [birthLocation setSubtitle:
[NSString stringWithFormat:@"%f,%f",
            [[listItems objectAtIndex:2] doubleValue],
            [[listItems objectAtIndex:3] doubleValue]]];
        } else {
         birthLocation = [[MapAnnotation alloc] initWithCoordinate:
            CLLocationCoordinate2DMake([
[listItems objectAtIndex:2] doubleValue],
 [[listItems objectAtIndex:3] doubleValue])
                title:_searchBar.text subtitle:
[NSString stringWithFormat:@"%f,%f",
                [[listItems objectAtIndex:2] doubleValue],
                [[listItems objectAtIndex:3] doubleValue]]
         ];
        }
        [mk_mapView addAnnotation:birthLocation];
    } else {
        [self AlertWithMessage:@"No se pudo encontrar la dirección"];
    }
 
}

Este código se aplica a una vista MkMapView con una barra de búsqueda, además he creado una clase para anotaciones básica MapAnnotation que lógicamente hereda de MkMapAnnotation (no instanciable a menos que se cree otra de NSObject que herede de esta).

Debemos guardar una pequeña caché de anotaciones para hacer más eficiente la aplicación, podemos utilizar el array de la propia vista del mapa de MkMapView.

En el caso en que queramos desarrollar un evento que nos vaya actualizando la información encontrada sobre una región haciendo una geolocalización inversa podemos crear una clase parecida a ésta:

@implementation MyGeocoderViewController (CustomGeocodingAdditions)
 
- (void)geocodeLocation:(CLLocation*)location forAnnotation:(MapLocation*)annotation {
MKReverseGeocoder* theGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:location.coordinate];
 
theGeocoder.delegate = self;
[theGeocoder start];
}
 
// Delegate methods
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFindPlacemark:(MKPlacemark*)place {
MapLocation* theAnnotation = [map annotationForCoordinate:place.coordinate];
 
if (!theAnnotation) return;
 
// Associate the placemark with the annotation.
theAnnotation.placemark = place;
 
// Add a More Info button to the annotation's view.
MKPinAnnotationView* view = (MKPinAnnotationView*)[map viewForAnnotation:annotation];
 
if (view && (view.rightCalloutAccessoryView == nil)) {
view.canShowCallout = YES;
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
}
 
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFailWithError:(NSError*)error {
NSLog(@"Could not retrieve the specified place information.\n");
}
@end
 
@implementation MKMapView (GeocoderAdditions)
- (MapLocation*)annotationForCoordinate:(CLLocationCoordinate2D)coord {
// Iterate through the map view's list of coordinates
// and return the first one whose coordinate matches
// the specified value exactly.
id theObj = nil;
 
for (id obj in [self annotations]) {
if (([obj isKindOfClass:[MapLocation class]])) {
 
MapLocation* anObj = (MapLocation*)obj;
 
if ((anObj.coordinate.latitude == coord.latitude) &&
(anObj.coordinate.longitude == coord.longitude))
{
theObj = anObj;
break;
}
}
}
 
return theObj;
}
@end

En el framework MapKit de Google se utiliza la proyección Mercator, que es un tipo específico de proyección cilíndrica para todo el globo.
Esto es muy útil para poder realizar una navegación fácil por las imágenes de un mapa tanto en longitud y latitud como en altura, esto se especifica con los tipos de datos para coordenadas que se especifican con CClocationCoordinate2D y las áreas con MKCoordinateSpan y MKCoordinateRegion.
Un punto del mapa es un par de valores (x, y) en la proyección Mercator. Se utilizan para simplificar los cálculos matemáticos entre puntos y áreas. Los puntos se especifican con MKMapPoint y las áreas con MKMapSize y MKMapRect. Un punto es una unidad gráfica asociada con el sistema de coordenadas de un objeto UIView. Por lo tanto, se puede asociar directamente la vista del mapa con la interfaz. Los puntos se especifican mediante CGPoint y las áreas mediante MKMapSize y MKMapRet.

Si queremos pasar al sistema de posicionamiento de un GPS estándar, podéis usar esta función de una de mis aplicaciones:

- (NSString*) toDegreesMinutesSeconds:(CLLocationCoordinate2D) 
 nLocation {
//Longitud: N, Latitud: W
int degreesW = nLocation.latitude;
double decimalW = fabs(nLocation.latitude - degreesW);
int minutesW = decimalW * 60;
int degreesN = nLocation.longitude;
double decimalN = fabs(nLocation.longitude - degreesN);
int minutesN = decimalN * 60;
return [NSString stringWithFormat:@"%dN%d;%dW%d",degreesW, 
 minutesW,degreesN,minutesN];
}

Por último sólo me queda recordaros los conceptos básicos de la geolocalización y posicionamiento con CoreLocation y MapKit de Google:

  • Se puede añadir un MapView mediante el Interface Builder.
    • De manera programática, se debe crear una instancia de MKMapView e inicializarlo mediante el método initWithFrame: y añadirlo como una subvista dentro de la jerarquía de vistas.
  • Propiedades principales de MapView
    • region (tipo MKCoordinateRegion).
    • Define la parte visible del mapa. Es posible cambiar esta propiedad en cualquier momento, asignando a ésta un valor.
    • centerCoordinate. Define la posición central del mapa
  • Anotaciones
    1) Definir un objeto de anotación concreto.
    • Es posible utilizar MKPointAnnotation para crear una anotación simple. Contiene un popup con un título y un subtítulo.
    • Definir un objeto personalizado que siga el protocolo MKAnnotation
    2) Definir una vista de anotación para presentar la información en la pantalla.
    • Si la anotación se puede representar con una imagen estática, se debe crear una instancia de MKAnnotationView y asignar la imagen a la propiedad image.
    • Si se desea crear la anotación de chincheta, crear una instancia de MKPinAnnotationView.
    • Si la imagen estática es insuficiente, se puede crear una subclase de MKAnnotationView e implementar el código de dibujado para representarla.
    3) Implementar el método mapView:viewForAnnotation: en el delegate del MapView.
    4) Añadir la anotación usando el método addAnnotation: o addAnnotations:.
  • Overlays
    • 1) Definir el Overlay apropiado (MKCircle, MKPolygon, MKPolyline o una subclase de MKShape o MKMultiPoint).
    • 2) Definir una vista para representar el overlay en la pantalla.
    • 3) Implementar el método mapView:viewForOverlay en el MapView delegate.
    • 4) Añadir el objeto al mapa mediante addOverlay:.

Y hasta aquí la entrega de este curso, los ejercicios de esta entrega son seguir el tutorial siguiente: introducción a MapKit.

« Ahora podéis volver al índice del Curso de aplicaciones de iOS

Configuración y traducción de una aplicación de iOS

En la entrega anterior del Curso de Aplicaciones de iOSvimos como guardar datos persistentes, pero ¿qué pasa si lo que queremos guardar son de muchos tipos y en distintas partes del código?, ¿escribimos una clase con métodos estáticos que acceda a un modelo de datos configuración con valores transformables?…vale, esto se podría hacer pero tenemos una opción más sencilla y que además se puede utilizar en la interfaz de configuración del propio iOS especialmente dedicada a nuestra aplicación.

Preferencias de la aplicación iOS

Un mundo en un diccionario

Estamos hablando de las preferencias por defecto del usuario. En otras palabras, vamos a usar la clase NSUserDefaults, que accede las preferencias que creemos o bien por código o bien con el diseñador de “Settings Bundle”, estas quedan almacenadas en un fichero plist (xml), donde se define cada elemento como un diccionario que contiene los diferentes parámetros en tipos de datos que son diccionarios internamente.

Ejemplo de creación: File -> New File -> Resource -> Settings Bundle.

Normalmente el nombre que se le suele dar al fichero es “Root.plis”, la edición dentro de XCode de este tipo de ficheros de configuración viene a ser algo así:

Estos diccionarios tienen unos tipos de atributos, los siguientes:

  • Type: tipo de la preferencia
  • Title: título de la preferencia, se utiliza para codificar las traducciones también (en lugar de Key)
  • Key: clave para recuperar la preferencia desde el código
  • DefaultValue: valor por defecto
  • IsSecure: se utiliza para campos de texto como contraseñas
  • KeyboardType: tipo de teclado, puede ser Alphabet, NumbersAndPunctuation, NumberPad, URL y EmailAddress (al igual que en las preferencias de Interface Builders para campos de texto)
  • AutocapitalizationType: se especifica como None, Sentences o Words y convierte lo elegido a mayúsculas
  • AutocorrectionType: si se pone como Yes entonces se utiliza el corrector automático de iOS en el campo

Veréis que existen varios tipos de datos a la hora de añadir un diccionario que contiene un elemento de preferencias, estos tipos pueden ser:

  • PSTextFieldSpecifier: campo de texto, para un usuario o password, o cualquier otro posible uso
  • PSTitleValueSpecifier: es un título, de sólo lectura, puede preceder a otro campo que no tenga título propio
  • PSToggleSwitchSpecifier: elemento ON/OFF, es decir, puede servir como booleano
  • PSSliderSpecifier: campo sin título, sirve para establecer un valor numérico con una escala, tiene un máximo y un mínimo, además del valor por defecto también podemos especificar una imagen para cada lado de los límites
  • PSMultiValueSpecifier: sirve para mostrar una lista de valores entre los que podemos elegir
  • PSGroupSpecifier: una manera elegante de separar cada zona de configuración haciendo un grupo (o subgrupo)
  • PSChildPaneSpecifier: al pinchar en este campo se abrirá una nueva página de configuraciones asociada (otro plist)
Aquí un ejemplo de las opciones:
En el caso que utilicemos un panel hijo el esquema sería algo parecido a esto:
Siempre podemos acceder a las opciones a través de la clase NSUserDefaults ,de la misma manera que funciona un diccionario (como un NSMutableSet), los objetos que guardemos tienen que ser serializables, como ocurría con el modelo de datos para poder utilizarse como un valor. En concreto, las preferencias se guardan desde NSUserDefaults -> standardUserDefaults, si creamos campos de preferencias para usuario,nivel, nombre y otro de password el código a usar es así:`

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
NSString *strUser = [defaults objectForKey:@&quot;usuario&quot;];
int level = [[defaults objectForKey:@&quot;nivel&quot;] intValue];
NSString *strName = [defaults objectForKey:@&quot;nombre&quot;];
NSString *strPass = [defaults objectForKey:@&quot;password&quot;];

Si cargamos la aplicación por primera vez, debemos guardar los valores, igual que antes pero usando la función

[[NSUserDefaults standardUserDefaults] setValue:(id) forKey:(NSString*)];

[NSUserDefaults standardUserDefaults] synchronize]; //imprescindible!

,evidentemente, si los tipos de datos son numéricos, tenemos que encapsularlos en una clase NSNumber, etc.

Para reiniciar los valores en el simulador (esto debemos hacerlo también para cuando cambiemos un fichero traducido de Interface Builder) , borramos el directorio ~/Library/Application Support/iPhone Simulator/User/Applications o bien, abrimos el simulador, y borramos la aplicación o por último, en el menú del simulador – > Restablecer contenidos y ajustes. Esta última opción borrará todas las compilaciones de XIB a NIB (ficheros de I.B.).

Info.plist

Este fichero, según la documentación de XCode, contiene la información de configuración de la aplicación (en un bundle), puede utilizarse para especificar el icono de la aplicación, incluso una cadena “audio” en la variable UIBackgroundModes para cuando la aplicación pasa a segundo plano y estamos haciendo streaming ,que no se corte.

O se puede indicar con la cadena “voip” para que se use un socket para VoIP (luego hace falta que se invoque al método setKeepAliveTimeout: handler para especificar la frecuencia con la que despertar la aplicación para que funcione bien la aplicación, por ejemplo, para cargar el buffer que se está reproduciendo).

Más información >

Traducciones de aplicaciones de iOS

Más diccionarios en ficheros de texto

Para realizar las traducciones utilizamos dos aplicaciones de XCode que extraen las cadenas de texto a traducir a todos los idiomas: ( ver recursos de traducción> ),

  • genstrings: extrae de los ficheros .m todos los usos de la función NSLocalizedString(@”LLAVE”, @”COMENTARIO PARA SABER DE QUÉ VA LA TRADUCCIÓN DE LA LLAVE EN SU SITIO”).
    En el código reemplazamos todas las cadenas a traducir @”Mi Cadena” , por NSLocalizedString(@”miCadenaX”, “Mi Cadena”)…fácil…
    Ejemplo de uso:, desde ~directorioDeMiProyecto$
    [sourcecode language=”bash”]
    genstrings -o es.lproj *.m

    toma como entrada todos los ficheros de implementación y busca NSLocalizedString, creando un fichero codificado con el juego de caracteres UTF-16 con líneas así:
    /* Comentario de la llave */
    “llave” = “Texto para la llave”;
    Una vez generados los ficheros para los .m en cada idioma, pues se traducen y luego se deben añadir los directorios al proyecto, si véis que no coge las cadenas de texto, quitad los directorios (en.lproj) sin borrarlos ,sólo la referencia, y luego añadirlos de nuevo

  • Truco para traducir los títulos de las preferencias (la configuración) de la aplicación: cuando creamos nuestro Settings.bundle ,dentro aparece un fichero llamado Root.plist , pues bien, vamos a crear un nuevo fichero en el proyecto ,de tipo Resource -> Strings File, le llamamos, claro, Root.plist, cuando nos pregunte si queremos sobreescribir le diremos que sí, después, en la ventana de inspección, en la pestaña de Localization añadimos los idiomas.
  •  ibtools:  Haremos lo mismo que en con el truco anterior, en la pestaña de Localization añadimos los idiomas a utilizar, después podemos usar una instrucción como esta para extraer todas las cadenas de texto (si no queremos traducir todo a mano con el editor del Interface Builder,claro), una vez añadidos los idiomas:
    [sourcecode language=”bash”]
    ibtool –generate-strings-file en.lproj/MiFichero.strings en.lproj/MiFichero.xib

    En el fichero strings estarán las cadenas de texto a traducir, una vez hecho, las escribimos de nuevo al .xib:

    [sourcecode language=”bash”]
    ibtool –write MiFichero.strings MiFichero.xib

    Más información de XCode 4 y ibtool >

Los strings se pueden enviar a empresas de traducción para que hagan su trabajo y luego con esos pasos ya está todo hecho. Para probar que los idiomas funcionan sólo hay que ir a la configuración del iDevice y cambiar en los Ajustes Generales el idioma en Internacional.

<< Volver al Curso de Aplicaciones de iOS
|| Ir al siguiente capítulo: Geolocalización con MapKit de Google y CoreLocation »

Persistencia de datos en iOS y Google Web Toolkit

En esta tercera entrega se explica brevemente como crear un modelo de datos usando XCode (internamente la gente de la manzana se ha apropiado de SQLite para hacerlo, han aprendido de la comunidad otra vez,al igual que en la época NeXT jeje) y cómo crear un servidor en la Google App Engine con las herramientas de Google Web Toolkit que nos proporcionará mediante servicios web la comunicación con una base de datos externa que desplegará datos a todos los dispositivos…lo que quiere decir es que puedes replicar un cambio desde cualquier lugar hacia todos a la vez de forma atómica…apasionante no?
Como no todo en esta vida es un camino de rosas, tratar con punteros y tipos de datos complejos tiene sus cosas, en XCode, o mejor dicho, con la versión de Objective C privativa que usamos para compilar nuestras aplicaciones para los dispositivos, tratar con la memoria es como jugar con granadas, desde que lanzas la granada al aire (reservas memoria) hasta que la recoges (liberas memoria), la parábola que surca en el aire podría ser un tanto extraña, también puede perderse la granada o ser reemplazada, meterse entre nubes o chocar con un avión que pasa jaja…con lo cual, nuestra tarea consiste en tratarla como a una mujer,delicadamente…para que no nos explote en la cara…Jugando con la memoria
Conceptos básicos para almacenar datos en iOS:

Core Data Foundation

  • Es un framework que garantiza la persistencia de los datos de nuestra aplicación
  • Genera una abstracción con clases que facilitan la implementación final del almacenamiento
  • Conecta la parte lógica con el modelo físico (ficheros, etc.)

Core Data Stack

  • El objeto que utilizaremos para gestionar colecciones de objetos es el Managed Object Context, también conocido en la jerga NeXT como NSManagedObjectContext, y representa el espacio de un único objeto, es decir, vamos  a utilizar este objeto para recuperar todo lo que tiene que ver con una entidad (una entidad es una tabla de la base de datos, también puede contener atributos en relaciones cuyos tipos sean otras entidades o tablas)
  • Con Managed Object Model (esquema de base de datos) guardaremos una colección de descripciones de entidades (nombres de tablas) pertenece a la clase NSManagedObjectContext  que relaciona Managed Object Context con el siguiente objeto que hace de puente:
  • Persistent Store Coordinator: guarda una colección de almacenes (Persistent Object Stores)

    en un conjunto de ficheros de datos persistentes (los ficheros de la base de datos, como en MySQL), su clase es NSPersistentStoreCoordinator y asocia los objetos de la aplicación con los registros de la BD.

Guía de uso del Modelo de Datos de iOS:

  1. Para hacer una consulta primero necesitamos crear las tablas, para ello lo que hacemos es crear un proyecto de tipo datos o bien añadimos un fichero al actual proyecto que sea XCode -> Core Data -> Data Model, en la pantalla de creación de entidades añadimos una tabla o entidad y la llamamos “Event”, por ejemplo. Dentro añadimos sus atributos con distintos tipos. Veréis que podéis usar un tipo “Transformable” que sirve para guardar Arrays, siempre y cuando los tipos que guarde el array sean soportamos por el protocolo del modelo de datos, además podemos crear relaciones como atributos que se conectan con otras entidades, aunque esto es más complejo, por ahora sólo veremos los casos simples. (ver más información >>)
    Podéis ver que el editor trae un soporte para la vista de un bonito Diagrama de Entidad/Relación de nuestra BD.
    Una vez creado el modelo vamos a New file -> Core Data -> NSManagedObject subclass (si lo que queremos es una clase que se encargue de realizar todas las operaciones setter & getter por nosotros y así asociemos el contenido al modelo de datos fácilmente con funciones, aunque también podemos hacerlo aún más automático con un framework como este: Fremont)
  2. Para crear objetos es necesario invocar a NSEntityDescriptor con el tipo de objeto y el contexto, veamos un ejemplo:(os recomiendo siempre escribir el código el inglés ;) . Supongamos que nuestra tabla (modelo de datos) event tiene los campos, longitud y latitud de tipo Float, un título (NSString -> String en Modelo de Datos), y una fecha de tipo date:
    //Crear y configurar una instancia de una entidad Event
    - (void) createEvent {
    Event *event = (Event *) [NSEntityDescription insertNewObjectForEntityForName:
    @"Event" inManagedObjectContext: managedObjectContext];
    //Crear un tipo de dato coordenada para mapas de google, así nos acostumbramos a usarlos:
    CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(37.123123, -3.321321);
    //Ahora lo tenemos muy fácil ,sólo tenemos que usar los setters y getters generados por XCode:
    [event setLatitude:[NSNumberWithFloat:coordinate.latitude]];
    [event setLongitude:[NSNumberWithFloat:coordinate.longitude]];
    [event setCreationDate:[NSDate date]]; //"date" es la fecha de hoy
    [event setTitle:@"Mi primer evento"];
    }

    y eso sería todo el código, ahora pasamos a grabar los datos para hacerlos persistentes

  3. Para guardar un objeto se utiliza el método savede NSManagedObjectContext, si ocurre algún error se guardará la información del mismo (el motivo por ejemplo) en la variable NSError dispuesta a tal efecto.
    NSError *error;
    if ([managedObjectContext save:&error]){ 
    //prestar especial atención al ampersand...
    //referencia de memoria!
    //Gestión del error aquí
    }

    Podemos intentar capturar errores con una captura de excepciones (@try { } @catch (NSException *exception) { } @finally { } ) pero esto no es recomendable, además de que debemos recordar la cadena de respondedores, aquí se aplica el mismo cuento y tendríamos que ir hacia arriba en la lógica de la programación para capturar la verdadera excepción de la pila de llamadas…cosa que es bastante tediosa, por eso es mejor pensar las cosas bien y hacerlas mejor jeje, con la práctica todo se consigue ;)
    Fetch Request -> execute!

    Si queremos recuperar el objeto no tenemos más que usar, como se hace en php y mysql una petición tipo “fetch”, es decir, con NSFetchRequest especificamos la entidad, aquí tenéis un ejemplo completo.
    En resumidas cuentas, hay que crear un objeto NSFetchRequest, reutilizamos el objeto de la descripción de una entidad y asociamos esta al primero con setEntity:

    NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
    //cuidado que esto no se libera. lo hace sólo...:O
    NSEntityDescription *entity = [NSEntityDescription 
    entityForName:@"Event" inManagedObjectContext: managedObjectContext];
    [request setEntity:entity];
    //Para realizar un ORDER BY fecha típico usamos un descriptor de ordenación:
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:
      @"creationDate" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [request setSortDescriptors:sortDescriptors];
    //ahora sí, liberamos los descriptores
    [sortDescriptor  release];
    [sortDescriptors release];
    //Con todo configurado, vamos a ejecutar la consulta y
    // guardar el resultado en una matriz modificable:
    NSError *error1;
    //Atención al parámetro "mutableCopy"!!
    NSMutableArray *results = [[managedObjectContext 
       executeFetchRequest:request &error1] mutableCopy];
    if ( results == nil ){
        //Manejar el error!
    }
    //Para borrar necesitamos un NSError igual que cuando guardamos con save
    NSError *error2;
    [managedObjectContext deleteObject:objetoEventoParaBorrar];
    if (![managedObjectContext save:&error2){
     //error...
    }

    Nunca os asustéis de un error como éste: significa que no habéis asociado bien el tipo de dato al diccionario usado para realizar una inserción en masa, ánimo!

Que los robots escriban código por nosotros: Servicios Web

Recordamos del curso de servicios web que escribir XML y código de un servicio web no es tarea de humanos, para eso existen frameworks que harán el trabajo duro por nosostros, Fremont es la parte cliente, en Objective C, para la parte del servidor tenemos los transformadores que ya vimos, BPEL, etc.

Gracias al uso de un servidor asociado a Google App Engine, crearemos un conjunto de servicios usando Google Web Toolkit 2.3.0 , que una vez probados en red local podremos desplegar en el servidor Java de GAE asociado a nuestra cuenta de usuario.

Pasos para la creación de un servidor de datos por medio de servicios web con Eclipse:

  1. Descargar el IDE Eclipse (Hellios SR2 por ejemplo) y el plugin para GWT.
  2. Crear un proyecto de prueba y ejecutarlo
    Ir a File -> New – > Project … -> Google -> Web Application Project . Le dáis un nombre al proyecto y al package sencillos, os recomiendo usar un área de trabajo nueva para no mezclar, aseguraos de que la opción de Google Web Toolkit está marcada y que estamos usando el SDK de GWT – 2.3.0 asó como el Google App Engine con su SDK (el que sea, en mi caso el 1.5.0) y marcad que genere el código de ejemplo para que todo fluya :) . Si todo ha ido bien debéis poder desplegar la aplicación en vuestra cuenta de google sobre el App Engine y ver el producto generado…ya podéis ser un 3% más felices hoy jeje
  3. Crear un paquete de servicios para nuestro servidor de aplicaciones de iPhone.
  • Pasos:
    • Crear paquete
      En el directorio src pinchamos con el segundo botón y hacemos New -> Package ,de nombre le damos servicios_iOS, por ejemplo. El primer servicio que vamos a crear es un servicio simple, que muestre una lista de eventos, pero antes debemos crear el modelo de datos que ha de persistir (guardarse) en el Data Store de Google Web Toolkit.
    • Crear modelo de datos
      Dentro de nuestro paquete hacemos click con el botón secundario del ratón y -> New -> Class, de nombre “Evento”, claro ,jeje…me gusta escribir todo el código de un servidor en inglés ya que es algo que veremos poco y puede que solamente nosotros…como opciones pues, es una clase pública, etc. Dentro de la clase añadimos ,como os imaginaréis,
      private Key id; private String titulo; private float latitud; private float longitud;
      y luego usaremos los generadores de setters y getters que tanto nos gustan de Java en Eclipse, así como los constructores.

      Hay un caso curioso, como veréis he puesto Key, en lugar de un int o un Long para la llave principal, esto es debido a que si queremos usar el objeto como algo persistente y almacenable dentro de otra clase, esta llave le permite indexar al objeto por lo que puede serializarse, convertirse en una cadena y quedar guardado en las bases de datos de Google.
      A cada campo le debéis añadir los protocolos antes de su declaración “@Persistent“, y a la llave principal le añadimos @Primary Key , y también: @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY).
      A la clase Evento además le añadimos el siguiente prefijo: @PersistenceCapable(indetityType = IdentityType.APPLICATION)
    • Añadir complejidad al modelo de datos
      Para aquellos que no se contentan con un modelo de datos simple y quieren asociar una lista de cosas (vaya usté a saber qué más cosas…) pueden usar el prefijo: @Persistent(embedded=”true”) para variables del tipo private List<TipoDeDatosPropio> miListaDeCosas; Sin olvidarse,claro, de que el tipo de datos propio ha de llevar nuestro querido prefijo @PersistenceCapable(indetityType = IdentityType.APPLICATION) y que ha de contener una llave o Key id para poder serializarse y empaquetarse dentro de un atributo persistente de otra clase persistente
    • Crear servicios que usen el modelo de datos
      Tenemos un modelo donde almacenar datos, utilizaremos un fichero HTML en el directorio /war/ sencillo donde colocar un formulario con los campos título, longitud,latitud, fecha y cuya acción (o action, con method=”POST”) irá a “/nombreProyecto/nombreAccionServicio”.
      Para crear el servicio que atenderá la petición vamos a nuestro paquete y -> new -> class -> “nombreAccionServicio”.  A la clase, le ponemos el prefijo @ServiceName(value=”nombreAccionServicio”) y hereda (con la palabra clave extends) de la clase HttpServlet, de modo que debemos sobrecargar la función
      public doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, DatastoreFailureException { //… },
      para que atienda la petición enviada desde el fichero HTML por el método POST. Igual que en PHP.
      Importante que la respuesta se haga de una manera parecida a esta:

      resp.setContentType("text/html");
      PrintWriter out = resp.getWriter();
      out.println("< !DOCTYPE HTML PUBLIC \"-W3C//DTD....."); //etc

      Una vez que hayamos creado nuestras inicializaciones de datos de nuestro modelo (instanciado las clases oportunas), debemos guardarlos usando un gestor de Persistencia, lo que viene a ser una clase de JDOHelper.getPersistenceManagerFactory(“transactions-optional”); que por medio de la función makePersistent(clase_instanciada) se almacenará (después debemos hacer un close(), claro jeje)

    • Añadir las rutas al war/WEB-INF/web.xml –> este es el enrutador de acciones, asocia un nombre de ruta en una url (con un patrón) a un servicio web (con una clase que extiende de HttpServlet). Para ello,abrimos el fichero y creamos dentro del web-app un hijo servlet que especifica la asociación con el programa usando la palabra clave servlet-name:nombreServicio y servlet-class: nuestra clase nombreAccionServicio.
      Por último, para el mapeado, es decir, la forma en que un patrón o expresión regular asocia una ruta url a nuestro servlet, añadimos un hijo de “web-app” llamado servlet-mapping con el mismo servlet-name:nombreServicio y url-pattern:/nombreAccionServicio
  • Repetir operación para crear servicios que generen listas de datos, en nuestro caso nos hará falta que se genere un listado XML de todos los <eventos> con sus etiquetas para cada campo que tiene el modelo de datos. Para ello, añadimos una función a la clase Evento que genere la cadena XML como un string en UTF8 y un Servlet que la muestre por pantalla, en este caso la cabecera ha de ser text/xml ;)
  • Más información en este tutorial >>

Análisis sintáctico: XML – Servicio Web – iOS

Como ya hemos visto antes el funcionamiento de la persistencia de datos en iOS con un Modelo de Datos, pasaré directamente al análisis de un XML, aunque esto debería hacerse automáticamente con un framework como Fremont, lo importante es que os quedéis con que las clases que permiten estas tareas son NSURL, NS/Mutable\URLRequest,y NSURLConnection. Estas descargan los datos y podemos realizar el análisis sintáctico con varios tipos de “parser”, SAX: envío de notificaciones a medida que el analizador sintáctico va leyendo la cadena XML recibida por NSURL, o bien DOM:  es un analizador que lee toda la cadena XML y construye su representación completa. Para utilizar estos analizadores debemos incluir al proyecto el fichero libxml2 de las librerías del SDK (tanto SAX como DOM), o bien NSXMLParser (sólo SAX). Aunque existen alternativas como TBXML, TouchXML, KissXML, TinyXML, GDataXML, etc. –> ver comparativa >>

Pasos para utilizar NSXMLParser:

  1. Crear el parser con la información recibida de NSURL.
    NSXMLParser *parser  = [[NSXMLParser alloc] 
    initWithData:xmlData];
     //recibidos por NSURL
  2. Asignar un delegado (delegate), una clase que se encarga de recibir los eventos,i.e., que hereda de <NSXMLParserDelegate>
    [parser setDelegate:self];
  3. Comenzar a parsear con [parser parse];

A partir de aquí, se trata de usar los eventos del delegado (eventos de NSXMLParserDelegate) que son: didStartElement -> empieza un elemento, didEndelement, didStartDocument, etc. de forma que al principio del documento inicializamos los datos (un array modificable, por ejemplo), y cada vez que encuentra un elemento de tipo titulo pues añade un elemento al array y luego al terminar un elemento de tipo evento, pues añade el array de propiedades de un evento al array de eventos…fácil…Importante implementar también la función:

- (void)parser:(NSXMLParser *) parser parseErrorOccurred:
  (NSError *)parseError;
ya que nunca se sabe qué errores se pueden cometer, por ejemplo que el documento no llegue completo, y la aplicación pende de un hilo por una mala gestión de este tipo de errores…algo que no da buena imagen por lo que hay que pulir estos detalles…

Uso de caché

La utilización de caché tanto para datos persistentes como para datos dinámicos es importante a la hora de realizar aplicaciones para móviles ya que está muy penalizado el uso de conexiones a Internet, es más rentable llegar a un equilibrio de carga de datos entre el servicio web y los usuarios de los dispositivos, por eso os presento las dos tareas a realizar en un proyecto serio…

Caché de imágenes con ASI Http Framework

En esta dirección encontraréis un framework sorprendente para realizar tareas con análisis sintácticos de XML, descarga de datos a través de urls, caché, etc.

Es una maravilla ver como funcionan los ejemplos, hay uno,especial de caché en el que se dispone una serie de descargas de imágenes y a cada una de ellas se le asocia un puesto en una clase cola-de-espera que tiene asociada una barra de progreso por lo que va actualizando el estado conforme se van descargando los datos…impresionante :)

Caché de datos con SQlite

Lo que podéis hacer es utilizar una caché para guardar la información en un modelo de datos, durante unos días en el iDevice, pasados esos días, vuelve a sincronizarse con el servicio de Google App Engine y reescribimos toda la estructura de datos del programa con nueva información, así nos ahorramos realizar peticiones al servidor continuamente, lo que ralentizaría mucho la carga y si la aplicación es muy usada puede generar un cuello de botella que ha de evitarse.

Para hacer esto nos viene bien los datos de configuración de un usuario, lo veremos en la próxima entrega del curso de aplicaciones para iOS, aquí mismo.

Recordar que podéis utilizar recursos como http://wiki.gnustep.orghttp://stackoverflow.com/ para solucionar vuestras dudas tanto con Objective C como con Java Google App Engine y GWT.

En cualquier caso, los ejercicios de esta entrega están claros cuáles son: desplegar una aplicación en GAE con GWT y pasar los datos a un modelo de datos de XCode (SQlite)…hay cientos de tutoriales en internet sin embargo si tenéis cualquier duda, mandadme el fichero y lo intentaré corregir.

<< Volver al curso de programación de aplicaciones de iOS | Siguiente lección: Configuración y traducción de una aplicación de iOS >>

Página 1 de 212
footer
jbelon © | sitemap.xml