La configuración de nuestra aplicación en el menu general

abril 26, 2011

Seguro que en múltiples ocasiones al entrar en la configuración de nuestro teléfono hemos visto listadas un montón de aplicaciones en la parte inferior de la pantalla. Esto es así porque a la hora de proporcionar un punto para configurar nuestra aplicación tenemos dos opciones, una es meterla dentro de la propia aplicación y la otra es integrarla en el menú de configuración del sistema.

Sin duda la opción de hacerlo nosotros en la aplicación puede parecer a priori la mas fácil, pues no tenemos que aprender una nueva forma de hacer las cosas, pero como veremos en esta entrada, integrarse en el menú global de configuración es sencillo y de esta manera nos quitaremos el gestionar una pantalla mas dentro de nuestro programa.

La única limitación que se nos impone es que las opciones que queramos configurar se adapten a los controles que el sistema nos ofrece. Aunque sinceramente, la mayoría de las opciones suelen ser cajas de texto, sliders o booleanos de ON/OFF.

El primer paso para añadir nuestro programa al menú de configuración, es incorporar a nuestro proyecto un bundle de tipo “settings”, dicho bundle va a tener uno o varios ficheros de tipo plist donde vamos a especificar los elementos de configuración de nuestra aplicación.

Normalmente la opción mas sencilla será usar el editor de ficheros plist integrado que viene en el xcode, pues en el caso de los settings, automáticamente nos detectará el tipo de elemento y nos mostrará las opciones disponibles.

Aún así, si nos gusta mirar el código de las cosas 😉 como seguro que es el caso, podemos ver el XML que se genera y tocar ahí mismo los valores de configuración de cada componente. Como podemos ver en la siguiente captura.

Para mas información sobre las propiedades de cada elemento, podemos visitar la documentación oficial de Apple, donde se describen todas posibilidades a la hora de describir la interfaz de configuración.

Una de las cosas mas interesantes es que no estamos limitados a una sola pantalla de configuración, podemos añadir una estructura jerárquica de pantallas de configuración.

Para ello, uno de los elementos del fichero plist, es “PSGroupSpecifier”, que nos permite especificar otro fichero plist que será el que contenga la pantalla en el siguiente nivel de jerarquía, así podemos crear la configuración tan compleja como la necesitemos

Una vez que tenemos nuestro bundle listo, el siguiente paso es recuperar esos valores en nuestro código. Una vez mas, este proceso es bastante sencillo gracias a la clase NSUserDefaults. Este método tiene los típicos getters y setters para obtener y configurar los valores de la configuración, así por ejemplo el código para recuperar el valor del campo de configuración que describimos en el plist seria:

NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
NSLog(@"Players: %@",[settings integerForKey:@"players"]];
NSLog(@"Active Push Notifications: %@", [settings boolForKey:@"enable_push"]

Como hemos visto, integrar nuestra aplicación en el menú global de configuración es tremendamente sencillo, y gracias a su potencia a la hora de describir nuestros parámetros de configuración, nos va a ahorrar el hacer dichas pantallas dentro de nuestra aplicación.

Anuncios

Mensajes entre clases usando NSNotificationCenter

abril 3, 2011

La orientación a objetos de los lenguajes actuales hace que nuestros programas estén hechos de clases, clases que se comunican entre ellas mediante mensajes. Estos mensajes según el lenguaje se manifiestan en llamadas a métodos y en otros casos, como es el de ObjetiveC, como realmente mensajes.

Seguramente en muchas ocasiones, nos hemos encontrado con el caso de que queremos recibir un mensaje, pero el objeto que lo envía, no necesariamente tiene que tener una referencia nuestra, o visto desde el otro lado, queremos enviar un mensaje y no sabemos quien estaría interesado en recibirlo, e incluso, ese alguien no guarda ninguna relación con nuestra clase, por lo que no seria correcto que tuviera una instancia nuestra.

Pongamos un ejemplo, tenemos una clase que se encarga de gestionar una comunicación, y por otro lado, en la parte de la vista que muestra la información que viene de esa comunicación, tenemos una tabla que muestra datos. Cada vez que se recibe un mensaje nuevo, la tabla tiene que repintar los datos.

Podríamos tener una instancia de la clase de comunicación en nuestra vista, pero estaría ligando dos clases que excepto por ese detalle, no tienen nada mas en común.

Una solución seria utilizar una capa intermedia, que podría ser el Modelo que propagara las notificaciones de inserción de datos a todos los niveles. Esta solución sería la que actualmente implementaríamos en muchos sistemas, pero requiere meter eventos y orientar nuestro diseño hacia este tipo de dependencias entre las clases.

La solución a la que se puede llegar con las notificaciones es desacoplar totalmente todas las clases del ejemplo. Usando este modelo, la clase de la vista se registraría en la cola de notificaciones diciendo que le interesa escuchar cuando llega un nuevo mensaje. Por su parte la clase que se encarga de controlar la comunicación, “posteará” un mensaje cuando reciba un mensaje.

De esta forma, todos los elementos están completamente desacoplados, y en el fondo, nadie necesita tener una instancia de otras clases. Incluso, si en algún momento una de ellas dejaría de existir, las otras clases podrían seguir funcionando sin problemas.

Después de todo este rollo ;), vamos a ver como es de fácil hacerlo en Cocoa y la cantidad de ventajas que aporta este enfoque en el diseño de nuestras clases.

En Cocoa y Cocoa Touch, existe una clase llamada NSNotificationCenter, dicha clase se va a encargar de gestionar un “centro” de notificaciones que es accesible desde todas las clases de nuestro programa, e incluso desde clases de diferentes programas.

El runtime creará el centro de notificaciones por nosotros, y por lo tanto accederemos a la clase usando un método estático.

Básicamente hay dos formas de interacción con el centro de notificaciones, por un lado, los “observers” se añaden como receptores de la notificación usando un nombre determinado. Por otro lado, los notificadores, notifican a los observadores usando ese nombre como clave.

Hay varios métodos para registrarse como observador, por ejemplo el método, addObserver:selector:name:object:, especifica la instancia, el selector de la clase que se ejecutará, el nombre de la notificación y opcionalmente la instancia que será capaz de enviar la notificación, si este parametro lo dejamos en “nil” no filtraremos por el objeto que envia la notificación. El otro método para añadirse como observador es: addObserverForName:object:queue:usingBlock:, donde en lugar de indicar un selector, se pasa un bloque de código que se ejecutará cuando se reciba una notificación. En este último método vemos el parámetro “queue”, que trataremos mas adelante.

Por otro lado, para “postear” una notificación, podemos usar dos métodos, postNotificationName:object:, donde indicamos el nombre de la notificación que estamos “levantando”, o bien, podemos usar el método, postNotification:, donde agrupamos la misma información en un objeto de tipo NSNotification.

Finalmente, los observers deben quitarse del centro de notificaciones cuando ya no quieren recibir más. Para ello se puede usar el método removeObserver: o removeObserver:name:object:.

Vamos a ver un ejemplo completo en acción.

/* Código de un UIViewController que ademas es un UITableViewDataSource */

- (id) viewDidLoad
{
  // ...
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  [center addObserver:self selector:@selector(logOn:) name:@"logged on" object:nil];

  // ...
}

- (void) logOn: (NSNotification*) theNotification
{
    // La clase encargada de la comunicación nos ha notificado de que
    // se ha producido la entrada al sistema
}

- (void)viewDidUnload
{
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  [center removeObserver:self];
}

/* Codigo de la clase que se encarga de gestionar la comunicación */

// ...
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  [center postNotificationName:@"loggedon" object:nil];
  // En este momento ya se habra ejecutado el codigo del ViewController que estaba
  // apuntado para recibir notificaciones.
// ...

Una duda que nos puede surgir es, como se ejecuta el código asociado a la notificación cuando el notificador postea un mensaje.

Realmente el código se ejecuta de forma síncrona y en el contexto de la instancia que se pasa cuando un observador se registra en el NotificationCenter, de esta manera, cuando el notificador postea el mensaje usando una de las variaciones del postNotification, se bloquea hasta que todos los métodos de los obsevers se ejecutan. Sin embargo, por si queda alguna duda, el método addObserver no bloque a la clase que se llama, el único momento de sincronismo se produce por parte de la clase que envía la notificacion.

Para que esta ejecución sea asíncrona, debemos usar colas de notificación, y si volvemos atrás, el parámetro queue del método postNotification es el que gestiona este detalle.

Así que os dejo con la intriga :D, y para dos futuros posts hablaremos de las notificaciones asíncronas usando colas de notificaciones y como enviar notificaciones entre procesos usando el NSDistributedNotificationCenter.