Protocolos
Los protocolos en iOS son métodos que actúan sobre un objeto y pueden ser implementados por cualquier clase. Básicamente tienen la declaración de una serie de métodos que ejecuta otra clase y se espera que le agreguemos funcionalidad según lo necesite cada una de las clases que invocan esta otra clase. Apple nos provee una serie de situaciones por las que probablemente deberíamos utilizar un protocolo:
- Declarar los métodos que se espera que otros implementen.
- Declarar la interfaz a un objeto, ocultando su clase.
- Capturar similitudes entre las clases que no están jerárquicamente relacionadas.
Para empezar debemos recordar que los protocolos no tienen una implementación de los métodos que ahí están declarados, por lo tanto su declaración sólo es necesaria en los archivos .h
. Veamos la sintaxis de un protocolo sencillo:
12345678 @protocol ProtocolName @optional// Lista de métodos opcionales@required// Lista de métodos requeridos y obligatorios @end
Un ejemplo que probablemente conocemos, si hemos seguido la serie de tutoriales iOS, son los UITableView ya que los métodos que implementamos para configurar, cargar con información nuestra tabla y manejar los eventos que éste lance son parte de los protocolos UITableViewDataSource y UITableViewDelegate. Algunos de estos métodos son los siguientes:
// Delegate - (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath; // DataSource Requeridos - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; // DataSource Opcionales - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;123456789 // Delegate-(void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath; // DataSource Requeridos-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; // DataSource Opcionales-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
Delegados
Los delegados (delegate) son un patrón en donde un objeto actúa en nombre de un coordinador asociado a otro objeto. Un delegate básicamente delega el control de la interfaz de usuario para un evento, o al menos se le pregunta para interpretar el evento de una manera específica en la aplicación, es decir mantiene una especie de vínculo con el objeto que le permite recibir los mensajes que el objeto genérico genera.
El funcionamiento de un delegate nos permite coordinar su apariencia y estado con cambios que se producen en otras partes de un programa, cambia generalmente provocado por las acciones del usuario. Más importante aún, los delegates hacen posible que un objeto pueda alterar el comportamiento de otro objeto sin la necesidad de tener una relación de herencia entre los objetos. El delegate es casi siempre uno de los objetos personalizados, y por definición, incorpora la lógica específica de la aplicación que el objeto genérico y el delegate no conoce.
Definiremos un delegate en este curso de la siguiente manera, y asociado a un protocolo que después la clase que lo invoque deberá implementar.
@interface ViewController ()< ProtocolName >{ id < ProtocolName> delegate; }123 @interfaceViewController()<ProtocolName>{ id<ProtocolName>delegate;}
Ejemplo
En este ejemplo desarrollaremos una aplicación que descargue un archivo desde una URL y nos indique el progreso de la descarga.
Supongamos que tenemos una clase con una serie de métodos que básicamente descarga un archivo en una URL y lo guarda dentro del dispositivo. Pero queremos saber e imprimir en la vista el progreso de la descarga paso a paso.
Creamos nuestro archivo protocolo.
Lo nombramos con el nombre que queramos para este ejemplo lo llamaremos FileDataAccessDelegate
Como se dan cuenta sólo se crea un archivo .h
y en este agregamos todos los métodos que vamos a utilizar tanto en la clase que descarga el archivo como en la que lo invoca.
1234567891011121314 // Método que se ejecuta una vez descargado y almacenado el archivo-(void)dowloadFinishLoading:(NSString *)filePath andName:(NSString *)name; // Método que se ejecuta cuando termina la descarga-(void)dowloadDidFinishLoading:(NSString *)name; // Método que se ejecuta una vez si ocurre un error-(void)dowloadFinishLoading:(NSURLConnection *)connection didFailWithError:(NSError *)error andName:(NSString *)name; // Método que se va ejecutando mientras se descarga el archivo-(void)dowloadChangeLoading:(NSURLConnection *)connection didReceiveData:(NSData *)data andProgress:(float)progress; // Método que se ejecuta al iniciar la descarga-(void)dowloadInitLoading:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
Luego agregamos a nuestra clase el delegate.
// No olviden importar el protocolo en el encabezado de la clase @property (nonatomic,strong) id< FileDataAccessDelegate >delegate;12 // No olviden importar el protocolo en el encabezado de la clase@property(nonatomic,strong)id<FileDataAccessDelegate>delegate;
Por último invocamos los métodos del protocolo en los puntos claves de la clase por ejemplo; cuando se esté descargando agregamos las siguiente líneas:
if ([self.delegate respondsToSelector:@selector(dowloadInitLoading:didReceiveResponse:)]) { [self.delegate dowloadInitLoading:connection didReceiveResponse:response]; }1234 if([selfdelegate respondsToSelector:@selector(dowloadInitLoading:didReceiveResponse:)]){ [selfdelegate dowloadInitLoading:connection didReceiveResponse:response];}
Listo!, ya tenemos nuestra clase para descargar un archivo con el protocolo y el delegate implementado, ahora solo nos queda algo de carpintería para invocar esta clase, descargar el archivo a nuestros dispositivos y hacer uso de los métodos del protocolo para ir actualizando la vista.
(IBAction)descargar:(id)sender{ FileDataAccess *file = [[FileDataAccess alloc] init]; [file descargarArchivo:@"http://codehero.co/oc-content/uploads/2013/08/Screen-Shot-2013-08-12-at-1.04.36-AM.png" nombre:@"imagen.png"]; file.delegate=self; } #pragma mark Delegete File -(void)dowloadFinishLoading:(NSString *)filePath andName:(NSString *)name{ NSLog(@"termina de descargar y guardar con exito"); _progreso.text = @"listo!"; } -(void)dowloadDidFinishLoading:(NSString *)name{ NSLog(@"termina de descargar"); } -(void)dowloadFinishLoading:(NSURLConnection *)connection didFailWithError:(NSError *)error andName:(NSString *)name{ NSLog(@"Error"); } - (void)dowloadChangeLoading:(NSURLConnection *)connection didReceiveData:(NSData *)data andProgress:(float)progress{ _progreso.text = [NSString stringWithFormat:@"%.2f%%",progress*100]; } - (void)dowloadInitLoading:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ NSLog(@"inicia de descargar"); }1234567891011121314151617181920212223 (IBAction)descargar:(id)sender{ FileDataAccess *file=[[FileDataAccess alloc]init]; [file descargarArchivo:@"http://codehero.co/oc-content/uploads/2013/08/Screen-Shot-2013-08-12-at-1.04.36-AM.png"nombre:@"imagen.png"]; filedelegate=self;} #pragma mark Delegete File-(void)dowloadFinishLoading:(NSString *)filePath andName:(NSString *)name{ NSLog(@"termina de descargar y guardar con exito"); _progresotext=@"listo!";}-(void)dowloadDidFinishLoading:(NSString *)name{ NSLog(@"termina de descargar");}-(void)dowloadFinishLoading:(NSURLConnection *)connection didFailWithError:(NSError *)error andName:(NSString *)name{ NSLog(@"Error");}-(void)dowloadChangeLoading:(NSURLConnection *)connection didReceiveData:(NSData *)data andProgress:(float)progress{ _progresotext=[NSString stringWithFormat:@"%.2f%%",progress*100];}-(void)dowloadInitLoading:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ NSLog(@"inicia de descargar");}
Construímos un poco la interfaz y probablemente tengamos como resultado algo así:
Conclusión
En esta lección, hemos aprendido un poco más de los protocolos y delegados en iOS, Aprendimos su funcionalidad y cómo se crea un protocolo desde cero. Te recomiendo eches un vistazo al repositorio establecido para este tema para comprenderlo mejor viendo la aplicación en funcionamiento.
Una vez más te recomiendo echarle un vistazo a la serie completa de iOS desde cero, así como a las otras series de CodeHero, agradeciendo de antemano todas sus dudas y comentarios en la sección de comentarios.
¡Hasta el próximo capítulo!