« Diciembre 2007 | Inicio | Febrero 2008 »

Enero 31, 2008

Entresijos. Haciendo la web. El nuevo proyecto de Design-Nation

Durante los próximos días, tomará cuerpo "Entresijos. Haciendo la web", el nuevo y por ahora último proyecto que sale de la cocina de ideas del emporio Design-Nation.

¿Qué es "Entresijos" ?

Se trata de un podcast, en formato entrevista y mesa redonda, de una duración no superior a media hora por episodio, por el que queremos que pase la mayor cantidad de profesionales que, de una u otra manera, desarrollen su actividad en relación con internet.

No es un podcast de y para programadores, sino que queremos que sirva como plataforma en la que todos los perfiles implicados en la construcción de internet (programadores, técnicos de sistemas, diseñadores, expertos en HCI, formadores, jefes de proyecto, bloggers, editores de webs de noticias, etc) y también aquellos que desde el otro lado de la barrera utilizan lo que los primeros construyen, puedan explicar en qué consiste su trabajo, cuáles son los retos con los que se enfrentan día a día, y cuál es la dirección que creen que debe tomar el futuro cercano, tanto de su parcela profesional como del negocio en general.

En el grupo de personas con el que estamos contactando hay programadores cliente y programadores servidor, técnicos de sistemas, diseñadores, animadores, expertos en e-learning, expertos en usabilidad,  directores de agencias de publicidad, o project managers.

Aprovechamos desde ya, para agradecer la colaboración de quienes en el último mes y medio nos han confirmado su deseo de participar. Y confiamos en que haya más gente colaborando a futuro.

Esperamos que os guste.

Enero 23, 2008

Bindings, NSArrayController y Value Transformers

Parece el título de una mala película de acción, de esas que estiran hasta más allá de lo razonable y lo creíble la trama de un comic, pero es aún peor, porque es el título de la última aventura de El Gran Dragón.

Y es que Bruce le ha ordenado que construya una prueba de concepto, una aplicación funcional, pero no muy compleja, que presente en un listado unos cuantos iconos. En concreto, los que corresponden con NSComputer, NSFolderBurnable y NSFolderSmart, que como El Gran Dragón bien sabe, Apple ha expuesto para su uso a partir de Leopard.

El Gran Dragón sabe que su Gran Maestro gusta de las aplicaciones sencillas y elegantes, de tener un núcleo funcional desde etapas muy tempranas sobre el que ir realizando los refinamientos necesarios y, por eso, su primera idea llega rápida como el ataque de una serpiente: "voy a construir una aplicación con un controlador, que tenga agregado un array mutable, array que controlaré con una instancia de NSArrayController, de forma que, por bindings, asigne el contenido del listado que constituye el interfaz de la aplicación a partir del contenido de ese array".

No está mal. Una solución sencilla, elegante, que utiliza en su beneficio muchos de los frameworks y metodologías de desarrollo para Mac OS X y cuya arquitectura sigue las líneas maestras de lo que se consideran "buenas prácticas".

Por tanto, El Gran Dragón comienza por crear el proyecto, como siempre, y por crear el controlador de la aplicación, una clase llamada AppController, que agregará un array mutable. La cabecera de esa clase sería por tanto:

#import


@interface AppController : NSObject
{
NSMutableArray *iconsCollection;
}

@property(copy, readwrite) NSMutableArray *iconsCollection;

@end

El interfaz de la aplicación tampoco tiene mucha complicación. Basta con crear en Interface Builder una instancia de NSObject y asignarle su tipo al controlador de la aplicación, y construir un interfaz con un NSTableView con una única columna, sobre la que El Gran Dragón arrastra una instancia de NSImageCell, para de esa forma poder presentar imágenes en la tabla.

interfaz_transformer.png

Bien, se dice a sí mismo el Gran Dragón. "Como tengo un controlador de la aplicación responsable de crear un array mutable, si consigo colocar en ese array mutable las imágenes que quiero presentar en el interfaz, sólo tendría que asignar el binding correspondiente en Interface Builder para que mi aplicación presentara las imágenes".

Como ya era tarde, El Gran Dragón se marchó a la cama sonriente, pensando que había encontrado la solución a su problema. Sin embargo, a altas horas de la madrugada, su maestro se le apareció en sueños: "Gran Dragón, ¿acaso has olvidado todo lo que te he enseñado? De verdad crees que es necesario crear esas imágenes? ¿Y si quisieras serializar el modelo a disco? No entiendes que no es necesario crear tantas instancias de NSImage, sobe todo si las puedes crear en tiempo de ejecución? ¿No te parece?"

El Gran Dragón despertó sudoroso, pero con una mirada de gran determinación. "¡Es cierto! Puedo crear las instancias de NSImage al vuelo, lanzándole un mensaje como éste!":

[ NSImage imageNamed: @"NSComputer" ];

Por tanto, sólo necesitaría guardar en el modelo las cadenas de texto con los nombres de las imágenes a crear. Pero, ¿cómo hago para convertir esa cadena de texto en una instancia de NSImage, que es a lo que necesito bindar la columna de mi tabla?

Pues utilizando un Value Transformer. Un Value Transformer es una clase que ejerce como mediador en el mecanismo de bindings. Si esa clase existe, antes de pasarle al interfaz lo que debe dibujar, se le pasa al mediador el valor extraído del modelo, y es el mediador el que, tras hacer lo que deba con ese valor, quien le termina por dar al interfaz lo que éste necesita.

En este caso, el interfaz (la columna de la tabla) necesita instancias de NSImage, mientras que lo que El Gran Dragón va a guardar en el modelo con cadenas de texto. Por tanto, el Value Transformer será el que cree las instancias de NSImage necesarias a partir de ese valor de texto.

Lo primero es crear la entidad básica del modelo. El Gran Dragón ha decidido, en un alarde de originalidad, llamarla AppEntity:

#import


@interface AppEntity : NSObject
{
NSString *iconName;
}
@property(copy, readwrite) NSString *iconName;
@end


La implementación:

#import "AppEntity.h"


@implementation AppEntity
@synthesize iconName;
@end


Ahora, El Gran Dragón escribe la implementación de su controlador para que, al crearse, éste cree las tres instancias de esa entidad que El Gran Maestro le ha pedido. Por tanto, AppController.m será:

#import "AppController.h"
#import "AppEntity.h"
#import "DNIconValueTransformer.h"


@implementation AppController
@synthesize iconsCollection;
-(id) init
{
self = [ super init ];

NSValueTransformer *iconTransformer = [ [ DNIconValueTransformer alloc ] init ];
[ NSValueTransformer setValueTransformer: iconTransformer forName:@"IconImageTransformer"];

iconsCollection = [ [ NSMutableArray alloc ] init ];

AppEntity *entityOne = [ [ AppEntity alloc ] init ];
entityOne.iconName = @"NSComputer";
[ iconsCollection addObject: entityOne ];

AppEntity *entityTwo = [ [ AppEntity alloc ] init ];
entityTwo.iconName = @"NSFolderBurnable";
[ iconsCollection addObject: entityTwo ];

AppEntity *entityThree = [ [ AppEntity alloc ] init ];
entityThree.iconName = @"NSFolderSmart";
[ iconsCollection addObject: entityThree ];

return self;
}
@end

Como puede verse, al inicializarse el controlador también se registra el Value Transformer, asignándole como nombre IconImageTransformer.

El siguiente paso de El Gran Dragón, por tanto, es escribir esa clase, que tendrá la siguiente cabecera:

#import


@interface DNIconValueTransformer : NSValueTransformer
{

}

@end

Como se puede ver, el único misterio que tiene es que extiende de NSValueTransformer.

La implementación tiene un poco más de miga:

#import "DNIconValueTransformer.h"


@implementation DNIconValueTransformer

+ (Class)transformedValueClass
{
return [ NSImage class ];
}

+ (BOOL)allowsReverseTransformation
{
return YES;
}

- ( id ) transformedValue:( id )value
{
NSLog( @"transformador %@", value );

if( value != nil )
{
return [ NSImage imageNamed: value ];
}
else
{
NSLog( @"pasa por el transformador" );
return nil;
}
}

@end

Aunque tampoco es muy complicada. El método transformedValueClass devuelve el nombre del tipo de retorno del método que va a realizar la transformación: transformedValue. En este caso, lo que se va a crear son imágenes, por tanto devuelve NSImage.

El método de transformación en sí tampoco es muy complejo. Simplemente se crea la instancia de NSImage a partir del nombre de la imagen que se le pasa al transformador desde el modelo.

Ahora, de vuelta en Interface Builder, hay que crear una instancia de NSArrayController, y encargarla manejar la colección de iconos:

controlador_Array.png

El último paso es asignar el binding de la columna de la tabla.

table_column_bindings.png

Como se puede ver, se binda al valor de la propiedad iconName de la entidad del modelo, pero utilizando la transformación que El Gran Dragón registró previamente como IconImageTransformer.

De esa forma, el resultado final es el que se pretendía conseguir:

icons_final.png

Y se ha conseguido con un modelo ligero, sin gran consumo de memoria ni de recursos del sistema, que puede ser serializado fácilmente.

Es un paso más para El Gran Dragón en el largo camino de la sabiduría.

El proyecto completo puede descargarse de este enlace

Enero 08, 2008

Extensión por delegación


Gran Maestro: La prueba de hoy en el largo y tortuoso camino del conocimiento, Gran Dragón, es programar una vista clickable, que puedas reutilizar en todas las futuras pruebas con las que te vas a encontrar, y que sea capaz de notificar al controlador de la aplicación en la que se esté utilizando cada uno de los clicks que reciba.

Gran Dragón: Gracias, Gran Maestro, por proponerme un reto de tal importancia.

El Gran Dragón sabe que no puede traicionar la confianza ni las expectatias de su maestro. Sabe que ha comenzado un largo camino, un camino lleno de escarpadas montañas, cruzado por anchos y profundos ríos, un camino del que parten otros muchos caminos que le pueden distraer de su objetivo final: llegar al estado mental en el que sólo hay una forma de pensar; la que termina con la máxima cohesión y el mínimo acoplamiento.

Por eso, y porque El Gran Dragón cree que la mejor forma de programar es iterativa, produciendo desde la primera iteración algo funcional, aunque no esté necesariamente muy refinado, comienza su aventura declarando una subclase de NSView, que, simplemente, sea capaz de capturar los clicks de ratón que se produzcan sobre ella. Además, El Gran Dragón quiere proporcionar feedback visual de las interacciones con su vista, por lo que va a hacer que la vista cambie de color, del gris en reposo, a un gris más claro cuando sea clickada.

Por tanto, la cabecera de su vista será:

#import


@interface DNClickableView : NSView
{
BOOL mouseIsPressingMe;
}

@end


Al inicializar la instancia de su clase, el Gran Dragón hará que la variable booleana valga false. Cada vez que redibuje el contenido de su vista, comprobará el valor de esa variable: si es true, el fondo será gris claro, y si es false, gris:

- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];

if (self)
{
mouseIsPressingMe = NO;
}

return self;
}

- (void)drawRect:(NSRect)rect
{
if ( mouseIsPressingMe )
{
[ [ NSColor lightGrayColor ] set ];
}
else
{
[ [ NSColor grayColor ] set ];
}

NSRectFill( rect );
}


Para capturar los eventos de ratón sobre esa vista, basta con declararlos, sobreescribiendo por tanto los correspondientes a NSView, en la implementación de la subclase:

- ( void ) mouseDown: ( NSEvent * ) theEvent
{
mouseIsPressingMe = YES;
[ self setNeedsDisplay: YES ];
}

- (void)mouseUp:(NSEvent *)theEvent
{
mouseIsPressingMe = NO;
[ self setNeedsDisplay: YES ];
}

Por tanto, la declaración completa de la implementación de la clase sería:

#import "DNClickableView.h"


@implementation DNClickableView

- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];

if (self)
{
mouseIsPressingMe = NO;
}

return self;
}

- (void)drawRect:(NSRect)rect
{
if ( mouseIsPressingMe )
{
[ [ NSColor lightGrayColor ] set ];
}
else
{
[ [ NSColor grayColor ] set ];
}

NSRectFill( rect );
}

#pragma mark -
#pragma mark mouse handling

- ( void ) mouseDown: ( NSEvent * ) theEvent
{
mouseIsPressingMe = YES;
[ self setNeedsDisplay: YES ];
}

- (void)mouseUp:(NSEvent *)theEvent
{
mouseIsPressingMe = NO;
[ self setNeedsDisplay: YES ];
}
@end


El Gran Dragón sabe que debe testar mucho y desde muy pronto, por lo que, para probar la funcionalidad de su clase, declara un controlador con un outlet hacia su vista, y compila y prueba el proyecto (El Gran Dragón da por supuesto que le lector sabe cómo hacer eso en XCode 3; no obstante, se puede encontrar más información sobre cómo hacerlo aquí)

Bien, el Gran Dragón está satisfecho de su primera iteración. Tiene una aplicación que compila aunque no cumple con uno de los requisitos pedidos: que la vista notifique al controlador los clicks recibidos.

Ahora ha llegado el momento de meditar, con la cabeza fría, sobre los diferentes caminos que se presentan ante el Gran Dragón.

El primer camino, el más obvio, y probablemente el más sencillo y rápido de implementar sería declarar una variable de clase en la vista, en la que guardar una referencia al controlador, de forma que, al capturar el clic, se envíe un mensaje al controlador de la aplciación.

No está mal, piensa El Gran Dragón. Sin embargo, ¿hasta qué punto esa solución es portable? ¿Hasta qué punto esa misma vista se podría reutilizar en más proyectos?

El Gran Dragón sonríe, con la satisfacción de saberse en el buen camino. Sabe que esa primera solución, la más rápida, es, probablemente, la menos adecuada. En primer lugar, porque implica que tanto la vista como el controlador tengan una referencia cruzada el uno al otro.

En segundo lugar, porque si se hacen las cosas como se debería, la referencia al controlador que se guarda en la vista debería estar declarada con el tipo del controlador. Lo que no la hace precisamente portable. Claro, que eso se podría solventar haciendo que esa referencia sea de tipo un protocolo que implemente el controlador. Lo que tampoco es bastante portable, porque obliga a que el controlador de cualquier aplicación que quiera utilizar esta vista deba implementar ese protocolo.

El Gran Dragón, por tanto, deshecha la primera solución.

La segunda solución que le viene a la cabeza es utilizar una notificación, de forma que la vista lance esa notificación (lo que en la mayoría de lenguajes se llama un evento), que será escuchada por el controlador.

El problema en esta segunda solución es que la implementación de las notificaciones en Objective-C es un poco particular. Las notificaciones se emiten y se reciben a través de un singleton (NSNotificationCenter) de forma que es relativamente sencillo pisarse un pie con el otro, y hacer que una notificación sea escuchada por quien no debe.

Además, las notificaciones hacen el código un poco más difícil de leer, y tampoco es que sean demasiado amigables a la hora de pasar parámetros.

El Gran Dragóm, por tanto, inmerso como está en la búsqueda de la elegancia en su código, deshecha la segunda solución.

La tercera solución, sin embargo, es utilizar uno de los patrones más comunes en Cocoa: la delegación.

La extensión por delegación no es más que una forma de composición un poco más sofisticada. Una clase, la que se quiere extender, puede delegar detalles concretos de implementación (lo que es susceptible de cambiar entre una utilización y otra de la clase) en otras clases, de forma que, en tiempo de ejecución, se pueden delegar responsabilidades en clases concretas declaradas a ese efecto.

Ese patrón es muy sencillo de implementar en Objective-C, como bien sabe El Gran Dragón, gracias a la naturaleza tan dinámica del lenguaje, y a que siempre se puede saber si una clase va a poder responder o no a un mensaje.

El Gran Dragón comienza, por tanto, la escalada de la delegación declarando una variable de clase en la vista, junto con sus accesors:

#import


@interface DNClickableView : NSView
{
BOOL mouseIsPressingMe;
id delegate;
}

- (id) delegate;
- (void) setDelegate: (id) newDelegate;

-(void) viewWasClicked;
@end

Además, declara un método que será ejecutado cuando se haga click en la vista (viewWasClicked)

Por tanto, los métodos dedicados a la captura de los clics quedarían así:

- ( void ) mouseDown: ( NSEvent * ) theEvent
{
mouseIsPressingMe = YES;
[ self setNeedsDisplay: YES ];
}

- (void)mouseUp:(NSEvent *)theEvent
{
mouseIsPressingMe = NO;
[ self setNeedsDisplay: YES ];

[ self viewWasClicked ];
}

Los acessors para el delegate:
- (id)delegate
{
return delegate;
}

- (void)setDelegate:(id)newDelegate
{
delegate = newDelegate;
}

El método que gestiona los clicks:

-(void) viewWasClicked
{
if( [ delegate respondsToSelector: @selector( viewWasClicked ) ] )
{
[ delegate viewWasClicked ];
}
}

Ahí es donde el Gran Dragón se siente más orgulloso. Para evitar estar enviando mensajes a nil, comprueba que lo que se haya asignado como delegate responde al mensaje viewWasClicked. En caso de responder, reenvía el mensaje a esa clase, y en caso de no responder a él, lo deja morir.

En el controlador, sólo faltaría asignar el delegate:

-(void) awakeFromNib
{
[ clickView setDelegate: self ];
}


Por tanto, la implementación completa de la vista sería:

#import "DNClickableView.h"


@implementation DNClickableView

- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];

if (self)
{
// Initialization code here.

mouseIsPressingMe = NO;
}

return self;
}

- (void)drawRect:(NSRect)rect
{
if ( mouseIsPressingMe )
{
[ [ NSColor lightGrayColor ] set ];
}
else
{
[ [ NSColor grayColor ] set ];
}

NSRectFill( rect );
}

#pragma mark -
#pragma mark mouse handling

- ( void ) mouseDown: ( NSEvent * ) theEvent
{
mouseIsPressingMe = YES;
[ self setNeedsDisplay: YES ];
}

- (void)mouseUp:(NSEvent *)theEvent
{
mouseIsPressingMe = NO;
[ self setNeedsDisplay: YES ];

[ self viewWasClicked ];
}


#pragma mark -
#pragma mark delegate handling

- (id)delegate
{
return delegate;
}

- (void)setDelegate:(id)newDelegate
{
delegate = newDelegate;
}

-(void) viewWasClicked
{
if( [ delegate respondsToSelector: @selector( viewWasClicked ) ] )
{
[ delegate viewWasClicked ];
}
}


-(void) dealloc
{
[ delegate release ];
[ super dealloc ];
}
@end


y la del controlador:

@implementation DNAppController

-(void) awakeFromNib
{
[ clickView setDelegate: self ];
}

#pragma mark -
#pragma mark DNClickableView delegate method

-(void) viewWasClicked
{
NSLog( @"DNAppController.viewWasClicked" );
}
@end


Mucho mejor. El Gran Dragón se siente satisfecho, porque aunque aparentemente haya implementado una solución similar a la primera que deshechó, ha ganado muchísimo en flexibilidad. En este ejemplo, el delegate se ha asignado al controlador, pero podría ser cualquier clase, instanciada directamente en el controlador, o producto de algún patrón de creación.

Sin embargo, el Gran Dragón sabe que su Gran Maestro, hombre noble y de infinita sabiduría pero muy exigente, no va a ver con buenos ojos que haya declarado la referencia al delegate como id, sin más.

Objective-C soporta protocolos, por lo que el método que se va a delegar, podría estar declarado en un protocolo, en un protocolo informal en realidad. ¿Porqué informal? Porque de esa forma no se obliga a nadie que lo cumpla a que implemente todos los métodos declarados en el mismo.

Por cierto, El Gran Dragón sabe que su solución está un pelín obsoleta con la introducción de Objective-C 2.0, ya que los ingenieros de Apple recomiendan que en la nueva iteración del lenguaje no se utilicen protocolos informales, sino que prefieren que se utilicen protocolos formales con métodos opcionales.

No obstante, El Gran Dragón quiere terminar con el proyecto, porque fuera del templo brilla el sol y le apetece salir a hacer fotos, así que comienza por declarar el protocolo informal, en la cabecera de la vista:

@interface DNClickableView : NSView
{
BOOL mouseIsPressingMe;
id delegate;
}

- (id) delegate;
- (void) setDelegate: (id) newDelegate;

@end

@interface NSObject ( DNClickableViewDelegate )
-(void) viewWasClicked;
@end


De esa forma, el compilador creerá que todos los objetos conforman ese protocolo, aunque en realidad, al ser informal, no tienen porqué implementar el método declarado en él.

El Gran Dragón está contento. Su subclase de NSView puede reutilizarse en todos los proyectos, porque no está vinculada a ningún controlador concreto, ni siquiera a un interfaz de éste, y lo que es mejor, la funcionalidad que se quiere asignar al click del ratón, que puede ser diferente en cada caso, puede estar encapsulada en una clase que se le proporciona a la vista, sin que ésta sepa nada de ella, ni de sus detalles de implementación, ni de su forma de creación.

El Gran Maestro, mientras tanto, mira orgulloso a su pupilo, pensando ya en el próximo reto...

Enero 06, 2008

Instalación de git en Mac OS X 10.5 Leopard

Como estoy hasta el gorro de las peleas a brazo partido con Subversion, he decidido comenzar a utilizar git para los proyectos nuevos.

La instalación de git en Leopard (Mac OS X 10.5.1) es muy sencilla.

El primer paso es descargar el tar con la última versión (a día de hoy la 1.5.3.7), y descomprimirlo (ya desde el Terminal):

tar xvzf git-1.5.3.7.tar.gz

A continuación, desde la carpeta generada al descomprimir el tar:

make configure
./configure --prefix=/usr/local
make all
sudo make install

Y listo. Ya se pueden borrar tanto el tar como la carpeta donde éste se descomprimió.