iOS Práctico, programación y otras cosas
Archives: Programación para iOS
Literales en Objective-C
- 18 marzo 2013
- Aspectos Básicos, iOS, Objective-C
- 4 comments
Los literales son simplemente unos valores que los programadores podemos escribir “tal cual” en el código. En Objective-C (y por lo tanto en C) ya conocemos algunos ejemplos de esto con los valores primitivos:
float altura = 23.5;
int piezas = 20;
Estos son: un literal float y un literal int respectivamente. Estos literales son comunes en la mayoría de los lenguajes, no obstante Objective-C tiene sus propios literales a parte de los mencionados, concretamente los que están basados en objetos:
NSString *tarea = @”Buscar piso”;
Claro, a simple vista podemos decir: Vaya chorrada, lo he usado mil veces!, pero detente un momento a pensar en el ahorro que implica en líneas de código el hecho de tenerlos: como escribirías un número “sin números”?, algo aún más terrorífico, plantearse escribir un string de miles de caracteres “sin strings”.
Dicho esto, es fácil darse cuenta de que los literales son una pieza fundamental de cualquier lenguaje por lo que nos ahorran, a parte de por los casos vistos, por la legibilidad en el código, así que vamos a conocer unos cuantos más pero específicos de Objective-C.
La nueva moda en literales
Hasta la salida de la versión 4.0 del compilador LLVM de Apple, Objective-C aún estaba un poco en pañales con respecto a lo que se refiere a literales. Mientras lenguajes basados en C como Perl o Python ya habían incluido literales para colecciones y más, Objective-C se resistía. Ahora eso ya es parte del pasado y vamos a ver cuales son las novedades en literales.
Literales para colecciones
NSArray
Clang, el front end de LLVM, introdujo la sintaxis @[ ] para definir arrays, en el cual solo se deben incluir objetos separados por comas. Ojo!, solo objetos, nada de tipos escalares. Así, lo que antes escribíamos así:
NSArray *ciudades = [NSArray arrayWithObjects:@"Barcelona", @"Lima", @"Lyon", nil];
Ahora nos queda así:
NSArray *ciudades = @[@”Barcelona”, @”Lima”, @”Lyon”];
Al ser una característica del lenguaje ya no es necesario definir el centinela nil, de hecho si lo pones como valor, tu ordenador explotará, ya que se realiza una validación como para el método [NSArrayarrayWithObjects:count:], en el que se requiere que ningún objeto sea nil. Por lo tanto si quieres pasar nil como valor tendrás que hacerlo con el objeto [NSNull null], que es su equivalente.
NSDictionary
Aquí se introduce la sintaxis @{ }, similar a la de JSON o Javascript, pero con el @ característico de Objective-C. Esta sintaxis crea un diccionario de pares key-value, donde key tiene que ser un objeto que implemente el protocolo NSCopying (los string de toda la vida, vamos!) y value, como en el caso anterior, sólo pueden ser punteros a objetos Objective-C. Algo como:
NSDictionary *usuario = [NSDictionary dictionaryWithObjectsAndKeys:@”ThXou”, @”nombre”, @”Barcelona”, @”ubicacion”, nil];
Ahora se escribiría:
NSDictionary *usuario = @{@”nombre” : @”ThXou”, @”ubicacion” : @”Barcelona”};
Como en el caso de los arrays, aquí tampoco es necesaria la centinela nil.
NSSet
Pobre, se olvidaron de él. No se ha introducido nada para esta colección, no obstante con los literales de NSArray se puede aprovechar mucho para NSSet cuando necesitamos pasarlos como argumentos de los métodos inicializadores y métodos de conveniencia.
Literales de NSNumber
Para los que no sabéis cómo definir NSNumber, pues es una clase que nos permite envolver valores escalares (otros literales de tipo int, bool, float, etc) en objetos Objective-C.
Ahora, cualquier valor escalar que empiece por el símbolo ‘@’ devolverá un objeto NSNumber inicializado con ese valor. Esto ya lo veíamos con los strings de C. Cuando escribíamos algo como:
NSString *queja = @"El billete de metro está caro";
En realidad estamos convirtiendo un string en C a un objeto NSString con codificación UTF-8. Este literal está desde los inicios, pero para los otros valores escalares usábamos el método numberWithTipo: para inicializar los objetos NSNumber. Ahora haremos:
NSNumber *bool = @NO; // es equivalente a [NSNumber numberWithBool:NO]
NSNumber *char = @'d'; // es equivalente a [NSNumber numberWithChar:’d’]
NSNumber *unsignedInt = @23U; // es equivalente a [NSNumber numberWithUnsignedInt:23U]
NSNumber *int = @23; // es equivalente a [NSNumber numberWithInt:23]
NSNumber *long = @23L; // es equivalente a [NSNumber numberWithLong:23L]
NSNumber *longlong = @23LL; // es equivalente a [NSNumber numberWithLongLong:23LL]
NSNumber *float = @5.2303F; // es equivalente a [NSNumber numberWithFloat:5.2303F]
NSNumber *double = @2.2808; // es equivalente a [NSNumber numberWithDouble:2.2808]
Me encanta este literal, por lo menos a mi me ayuda haciendo más legible el código, sobre todo cuando tengo que hacer una lectura rápida.
Expresiones “en caja” (Boxed Expressions)
Si la intuición os ha llevado a pensar: ¿Y qué pasa si hago @2+2, el compilador me lo pillará sin enfadarse?. Pues no, ya que hay una nueva sintaxis para esto y es envolver nuestras expresiones entre paréntesis: @(). Esto nos devolvería un objeto NSNumber inicializado con el resultado de la expresión que está entre estos. Y lo que antes hacíamos así:
NSNumber *piMedios = [NSNumber numberWithDouble:M_PI / 2];
Ahora lo hacemos así:
NSNumber *piMedios = @(M_PI / 2);
Mucho más claro y sencillo. Lo genial es que también funciona pasándole propiedades de algunos objetos Objective-C que devuelven valores escalares. Por ejemplo si tenías que guardar en Core Data el valor de un objeto UISwitch, lo hacíamos así:
tarea.completado = [NSNumber numberWithBool:mySwitch.on];
Ahora también lo puedes hacer simplemente así:
tarea.completado = @(mySwitch.on);
También funciona para enumeraciones:
typedef enum { Barcelona, Lima, Lyon } Ciudad;
NSNumber *ciudad = @(Lyon); // nos devolverá 1 y es equivalente a [NSNumber numberWithInt:((int)Lyon)]
En este caso, para poder usar algún valor de la enumeración tenemos que envolverlo también como si se tratara de una expresión para poder usarlo como literal.
Subíndices de objeto (Object Subscripting)
Esta última tanda de literales de la que vamos a hablar suple una necesidad en mi que vengo deseando ver desde que dejé C++ para embarcarme en Objective-C, y tiene que ver con la forma de acceder y obtener datos de colecciones, concretamente de arrays y diccionarios.
Subíndices para Arrays
Para los arrays podemos usar un index para referirnos a la posición de un objeto dentro de ese array, tal como se hacía en C con valores escalares:
NSArray *ciudades = @[@"Barcelona", @"Lima", @"Lyon"];
NSString *ciudad = ciudades[2]; // ciudad = @"Lyon"
Aquí se nos devuelve el elemento en la posición 2 que es @"Lyon". Esto el compilador lo traduce por su equivalente en Objective-C: [ciudades objectAtIndexedSubscript:2], lo cual es exactamente lo mismo que hacer: [ciudades objectAtIndex:2].
De la misma forma, si tenemos un array mutable, entonces podemos hacer asignación directa de valores con esta misma sintaxis:
ciudades[1] = @"Roma"; // el array quedaría: @[@"Barcelona", @"Roma", @"Lyon"]
El valor en la posición 1 es cambiado por el nuevo. Aquí el compilador hace una traducción al método [ciudades setObject:@"Roma" atIndexedSubscript:1]
Subíndices para diccionarios
Para los diccionarios en vez de usar un index usamos keys para obtener los valores:
NSDictionary *usuario = @{@"nombre" : @"ThXou", @"ubicacion" : @"Barcelona"};
NSString *ubicacion = usuario[@"ubicacion"]; // ubicacion = @"Barcelona"
El compilador hace la traducción a [usuario objectForKeyedSubscript:@"ubicacion"], el cual es a su vez equivalente a [usuario objectForKey:@"ubicacion"].
Como en los arrays, pasa lo mismo para los diccionarios mutables y podemos reemplazar el valor correspondiente a la key que referenciemos:
usuario[@"nombre"] = @"ThXou soy yo";
// el diccionario quedaría: @{@"nombre" : @"ThXou soy yo", @"ubicacion" : @"Barcelona"}
La traducción correspondiente es [usuario setObject:@"ThXou soy yo" forKeyedSubscript:@"nombre"].
Conclusión
Esta nueva sintaxis como se puede observar, ayuda a que nuestro código sea más legible, a la par que nos ahorra tiempo escribiendo sus métodos equivalentes. Va a ser hora de pasarnos a la nueva moda, nunca es tarde aunque ya lleve unos cuantos meses rulando por internet. Es muy importante recordar pasar únicamente objetos al momento de crear objetos usando literales, como también no pasar nunca un nil como un valor.
Fuentes:
- http://clang.llvm.org/docs/ObjectiveCLiterals.html
- http://www.mikeash.com/pyblog/friday-qa-2012-06-22-objective-c-literals.html
NSNotificationCenter y las notificaciones
- 21 febrero 2013
- iOS, iPhone, Objective-C, Programación para iOS, Tutoriales
- One comment
Vuelvo a la carga con los tutes sobre iOS después de un tiempo ausente por proyectos personales.
Esta vez os voy a hablar sobre otro tipo de notificaciones, diferentes a las notificaciones locales que vimos hace un tiempo. Estas nuevas notificaciones básicamente encapsulan información acerca de algún tipo de evento. Hay objetos que registran estas notificaciones en lo que podemos llamar una “tabla de notificaciones”, esta tabla está administrada por un centro de notificaciones, que es un objeto NSNotificationCenter. Luego tenemos objetos que se registran como “Observadores” de estas notificaciones, de manera de que cuando una de estas notificaciones es lanzada, todos los observadores “se enteran” de esto y normalmente llevan a cabo alguna acción.
Podría parecer un poco chungo, pero no lo es para nada. Te pongo un ejemplo práctico. Imagina un portal de noticias. Los usuarios entran a este portal y pueden suscribirse a las categorías de noticias que ellos prefieran. Una vez sale una noticia relacionada con categoría a la que el usuario se ha suscrito, entonces le llega un correo electrónico avisándole que hay una nueva noticia, así el usuario puede decidir que hacer en ese momento. Pues bien, si hacemos una comparación sacamos que el usuario que se suscribe a las noticias es el “Observador”, el portal de noticias sería el “centro de notificaciones” (NSNotificationCenter) y el correo electrónico sería la notificación lanzada por el centro de notificaciones.
A tocar código, que es lo que mola
Últimamente escribir tanto texto sin una sola línea de código me da alergia, así que vamos a escribir unas cuantas líneas.
De toda la clase NSNotificationCenter, normalmente solo vamos a usar 4 métodos. El primero es para inicializar nuestro centro de notificaciones. Cada aplicación viene con uno por defecto y para acceder a el usamos un método de clase que lo que hace simplemente es crear un singleton:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
Ya tenemos nuestra instancia en el objeto center. El segundo método nos va a permitir registrar una notificación en el centro de notificaciones. A este método le pasamos el nombre de nuestra notificación y un objeto que es el que envía la notificación, normalmente self:
[center postNotificationName:@"kTestNotification" object:self];
Se podrían separar estas notificaciones en 2 tipos: las personalizadas (como esta) y las que emite el sistema. En las que emite el sistema (por ejemplo cuando el dispositivo es girado o el teclado de un textField es mostrado), no necesitamos usar el método anterior ya que el sistema lo hace solo, nosotros solo tenemos que añadirnos como observadores de esas notificaciones y esperar, así que esto es lo que haremos ahora.
Registrándonos como observadores
Antes vimos el método para registrar una notificación llamada kTestNotification en el centro de notificaciones. Bien, ahora nos haremos “Observadores” de esta notificación para que cuando sea registrada (Osea, cuando sea ejecutado el método anterior), nosotros podamos llevar a cabo alguna acción en ese mismo instante:
[center addObserver:self selector:@selector(handleNotification:) name:@"kTestNotification" object:nil];
El gustillo de este tipo de notificaciones para mi está en que, suponiendo el caso de que tu aplicación tenga 100 controladores, no importa en cual de estos 100 controladores registres la notificación, añadiendo tu clase como observador serás capaz de enterarte cuando sea registrada, en cualquier momento y en cualquier controlador.
Cuando el centro de notificaciones avisa a los observadores sobre una notificación lo hace de manera síncrona. Esto quiere decir que vas a tener que esperar primero a que todos los observadores reciban sus notificaciones antes de poder hacer algo. Esto se puede solucionar registrando las notificaciones de forma asíncrona usando NSNotificationQueue en vez de NSNotificationCenter, pero esto ya es otro tema del que hablaremos en otra oportunidad.
Al añadirnos como observadores también definimos un método a ejecutarse cuando la notificación sea registrada:
- (void)handleNotification:(NSNotification *)notification
{
NSLog(@"Hey tu!, se ha disparado la notificación!");
}
Aquí simplemente mostramos por consola un texto, pero tu puedes usarlo para lo que quieras, cosas como mostrar un controlador, cerrar una conexión a Internet, etc.
Liberando la memoria
Lo único que nos queda ahora es liberar la memoria removiendo el observador que hemos asignado antes:
- (void)dealloc {
// liberamos la memoria que ocupa el observador
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc]; // quita esta línea si usas ARC
}
Como vez, no estamos utilizando el objeto center, y a decir verdad para ahorrarte una innecesaria línea de código te recomiendo no crear un objeto NSNotificationCenter, sino usar la forma: [[NSNotificationCenter defaultCenter] ... , como en el código de arriba.
Trabajando con el Social Framework de iOS 6: SLRequest, publicar en Twitter y obtener el Timeline
- 20 septiembre 2012
- iOS, iPhone, Objective-C, Programación para iOS, Tutoriales
- 9 comments
Siguiendo con los tutoriales sobre iOS 6, hoy nos toca Twittear (o como se escriba) y mostrar nuestro timeline de Twitter en una aplicación.
Antes cuando queríamos comunicarnos con Twitter y obtener datos y actualizarlos había que hacer una conexión a través de OAuth con unos tokens y secret-keys, esto normalmente se hacía más digerible usando algún framework o clase externa que servía de interfaz de conexión entre el cliente y el servicio. Desde la llegada del framework de Twitter y la integración con iOS 5 las cosas se pusieron extraordinariamente más fáciles, esto nos permitió el intercambio de datos con Twitter en tan solo pocos pasos.
En el Social Framework tenemos una clase llamada SLRequest, muy similar a TWRequest del framework de twitter. Esta clase encapsula las propiedades de una petición HTTP en métodos fáciles de utilizar, con los cuales enviamos peticiones a Twitter para poder obtener y actualizar datos de nuestras cuentas configuradas en el dispositivo.
Básicamente enviamos una petición HTTP con unos parámetros que configuran lo que queremos llevar a cabo en el servicio, si Twitter dice que no pasa nada, recibimos una respuesta con unos datos que tenemos que manipular y mostrar al usuario, caso contrario recibimos una información de error.
Autenticando la petición
Como les decía párrafos atrás, antes había que usar tokens y secret-keys para autenticarnos en Twitter y así poder validar nuestras peticiones. Con el Social Framework hacemos lo mismo pero de forma automática, más transparente al usuario, y en cierta forma al desarrollador, ya que en ningún momento tenemos que manipular tokens.
Desde iOS 5 tenemos el Accounts Framework, el cual provee un sistema centralizado de cuentas de usuario. A través de el se almacenan todas las cuentas de Twitter (Y de otros servicios) configuradas en el dispositivo: información de usuario y contraseña y demás, esto nos permite saltarnos esa típica ventana de inicio de sesión sin tener que preocuparnos por proveer un sistema para almacenar nosotros mismos las credenciales.
Hay pocas cosas que podemos hacer sin autenticación de usuario, y obtener el timeline es una de ellas. No obstante para hacer que esto funcione con cualquier cuenta, centralizaremos todas las peticiones en las cuentas obtenidas de la base de datos de cuentas del dispositivo. Empecemos:
Lo primero será importar los frameworks:
#import <Accounts/Accounts.h> #import <Social/Social.h>
Luego hacemos la mágia de obtener las cuentas del dispositivo con el Accounts Framework:
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
// creamos un objeto accountType especificando que solo queremos obtener las cuentas de Twitter
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType
options:nil
completion:^(BOOL granted, NSError *error)
{
if (granted)
{
// guardamos las cuentas de twitter en un array
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
// armamos la petición aquí
}
else {
NSLog(@"Error no se pudo acceder a las cuentas: %@", [error localizedDescription]);
}
}];
Lo que hacemos aquí es crear una instancia de la base de datos de cuentas. Luego a través de la clase ACAccountType decimos que solo queremos las cuentas de Twitter, pasándole la constante ACAccountTypeIdentifierTwitter como argumento en la inicialización. Acto seguido pedimos acceso a las cuentas con el método requestAccessToAccountsWithType:options:completion:. Este método tiene como argumento el bloque completion:, el cual es un handler (Un objeto ”manipulador” por así decirlo) en cuyos parámetros es devuelta la respuesta del método. Si todo va bien almacenamos todas las cuentas obtenidas en el array accountsArray o mostramos un error en caso contrario. Simple.
Recueda esta dinámica de ejecutar un método y recibir una respuesta para ser manipulada en un bloque, porque lo verás muy a menudo desde ahora.
Contruyendo la petición para obtener el timeline
Con el Social Framework es definitivamente mucho más fácil construir una petición HTTP. Sabemos que está compuesta por:
- Una URL que identifica la operación que queremos realizar en el servicio.
- Un método de petición, que puede ser GET, POST o DELETE.
- Y unos parámetros de configuración.
El método requestForServiceType:requestMethod:URL:parameters: pasa todos estos datos como sus argumentos y esto nos permite crear la petición en tan solo una línea de código si así lo deseamos.
Lo que nosotros queremos es obtener el timeline, por lo tanto necesitamos ir a la documentación oficial para ver lo que tenemos que usar. Al entrar en el enlace y seleccionar la operación de la que queremos ver los detalles (en este caso es: GET statuses/home_timeline), veremos en el apartado Resourse Information cierta información muy importante:

Tenemos el método de la petición (GET), el formato de respuesta (JSON) y el objeto de respuesta (Tweets). Luego está el apartado Resource URL que es la URL que le pasaremos y el apartado Parameters, que contiene los parámetros para configurarla. Usando esta información construimos la petición:
// guardamos la cuenta
ACAccount *twitterAccount = [accountsArray objectAtIndex:0];
self.cuenta = twitterAccount;
// creamos la petición
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1.1/statuses/home_timeline.json"];
NSDictionary *parametros = [NSDictionary dictionaryWithObjectsAndKeys:
@"25", @"count", nil];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodGET
URL:url
parameters:parametros];
// asociamos la cuenta a la petición
[request setAccount:twitterAccount];
Por motivos de brevedad tan solo utilizaremos la primera cuenta de las que hay en el array.
He creado la propiedad cuenta de tipo ACAccount, en ella almacenamos esta cuenta para posteriormente poder enviarla al controlador desde el que publicaremos un mensaje de estado (lo que es un Tweet) a nuestro timeline, esto más adelante.
Como pueden ver tenemos un diccionario para los parámetros. El mío solo tiene una key: count, esta nos permite limitar la cantidad de tweets que nos va a devolver el timeline que por defecto es 20, pero yo la he puesto a 25. En la documentación de la API están todos los parámetros que podemos usar para configurar la petición.
Lo siguiente es asignarle a la propiedad account de nuestra petición, la cuenta que hemos elegido del array y con la que queremos trabajar para mostrar el timeline y demás cosas.
Enviando la petición y manipulando los resultados
Una vez construida la petición procedemos a enviarla. Para esto usamos el método performRequestWithHandler: que envía la petición y recoge los resultados en su único argumento. Este es a su vez un bloque, el cual es ejecutado una vez están disponibles los datos de la respuesta.
// realizamos la petición especificando un método para manipular la respuesta
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (responseData != nil)
{
self.tweets = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
}
}];
Este bloque tiene 3 parámetros, el más importante es responseData, porque es el que va a contener los tweets. Estos tweets están en formato JSON como vimos antes, por lo tanto necesitamos parsearlos y así pasarlos a un formato manipulable en Objective-c. Para hacer esto existe la clase NSJSONSerialization, que coge los datos en NSData (en este caso responseData), los parsea y devuelve. Estos datos devueltos los almacenamos en el array tweets, que a continuación usaremos para mostrarlos al usuario.
NSJSONSerialization en nuestro práctico tutorial sobre el tema haciendo clic aquí.Mostrando los resultados
Bien, ya tenemos hecho casi todo el trabajo, ahora solo nos queda mostrar los resultados, y para esto tenemos el objeto tweetsTableView. Debido a que ya tenemos todos los Tweets en un array, es relativamente sencillo mostrarlos en el tableView, por eso vamos directamente a implementar los métodos convenientes:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.tweets count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
NSDictionary *tweet = [self.tweets objectAtIndex:indexPath.row];
cell.textLabel.text = [tweet objectForKey:@"text"];
cell.detailTextLabel.text = [[tweet objectForKey:@"user"] objectForKey:@"screen_name"];
// cargamos las imágenes de los que envían el tweet de forma asíncrona
dispatch_queue_t queue = dispatch_queue_create("com.thxou.totweet", NULL);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSURL *imageURL = [NSURL URLWithString:[[tweet objectForKey:@"user"] objectForKey:@"profile_image_url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_async(main, ^{
cell.imageView.image = [UIImage imageWithData:imageData];
});
});
dispatch_release(queue);
return cell;
}
Aquí hay algunas líneas que les sonarán a chino, no obstante explicaré un poco por encima que está sucediendo.
Al parsearse los datos estos son almacenados en el array tweets, pero los tweets dentro de el son almacenados en forma de diccionarios, fácilmente recuperables usando la clase NSDictionary. Entonces recuperamos cada campo de ese array en un diccionario que yo he llamado tweet para hacerlo más identificable (en realidad eso es lo que representa). Como ya saben, accedemos a los valores de un diccionario a través de keys, pero cuales son estas keys?, nos vamos a la documentación oficial y lo miramos allí. Lo que hacemos es simplemente mostrar el texto del tweet como título y el nombre “del que tweetea” como subtitulo en cada celda.
Para mostrar la imagen lo que hacemos es usar el GCD (Grand Central Dispatch) de Apple. A grandes rasgos explicarles que la descarga de datos de la red siempre debería de ser de forma asíncrona, esto es porque es un proceso que tarda un poco en llevarse a cabo y por lo tanto no puede hacerse en el mismo thread (hilo) ya que podemos bloquearlo, y esto dejaría inutilizable la interfaz de usuario hasta que se complete el proceso, cosa que por el bien de nuestros usuarios, no queremos. Pues este problema lo soluciona el GCD, haciendo que ciertos métodos se ejecuten de forma asíncrona (en otro hilo o thread), de esta forma evitamos bloquear la interfaz de usuario.
Enviando un tweet
Pues hacer esto es un poco más de lo mismo. Yo he creado un nuevo controlador para hacer esto llamado EnviarTweetViewController, el cual se muestra en una ventana modal y tiene un TextView y dos botones: uno para enviar el tweet y otro para cancelar la operación.
El de enviar el tweet ejecuta el método enviarTweet::
- (IBAction)enviarTweet:(id)sender
{
// comprobamos si el campo para escrbir el tweet no está vacío
if (![self.tweet.text isEqualToString:@""])
{
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1.1/statuses/update.json"];
NSDictionary *parametros = [NSDictionary dictionaryWithObjectsAndKeys:
self.tweet.text, @"status", nil];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodPOST
URL:url
parameters:parametros];
// Set the account used to post the tweet.
[request setAccount:self.cuenta];
// Perform the request created above and create a handler block to handle the response.
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
NSDictionary *resultado = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
}];
[self cancelar:nil];
}
}
Como podéis observar se hace exactamente lo mismo. Tan solo cambian los parámetros, la URL y el método de la petición, y todo esto lo podemos encontrar en la documentación oficial.
A diferencia del anterior, aquí no accedemos a todas las cuentas del dispositivo, sino que simplemente pasamos a este controlador la cuenta que hemos obtenido antes, así nos aseguramos de que la cuenta que está seleccionada es desde la que se envía el tweet y sobre la que se hacen las operaciones solicitadas. No hay misterio.
Conclusión
El procedimiento para llevar a cabo todas estas acciones en Twitter llevan la misma estructura. Tan solo varían los parámetros, la URL y el método de la petición. Por lo que, si queremos hacer cualquier cosa, tenemos que ir a la documentación y mirar lo que necesitamos. Luego reemplazar los datos que hemos visto antes con los nuevos, enviar la petición y mostrar al usuario los datos obtenidos. Así de fácil es trabajar con el Social Framework.
Ahora puedes pasarte por la documentación de Apple sobre este tema y también visitar nuestro tutorial sobre como parsear y manipular ficheros JSON de forma nativa y así extender un poco más tus conocimientos.
Trabajando con el Social Framework de iOS 6: Publicar en Twitter y Facebook
- 20 septiembre 2012
- iOS, iPhone, Objective-C, Programación para iOS, Tutoriales
- 5 comments
Aquí les traigo un tutorial bastante fresco sobre el recién nacido iOS 6. Como sabéis cada nuevo iOS trae consigo muchas novedades y este no va a ser la excepción, tenemos cientos de nuevas APIs y cambios con respecto a iOS 5, y por si fuera poco también tenemos 3 nuevos frameworks para estudiar e implementar en nuestras apps. Queda aún trabajo por hacer.
Hoy voy a comenzar con el primero de una serie de tutoriales para ir introduciéndonos en el mundillo de iOS 6 poco a poco. Empezaré con uno de los 3 nuevos frameworks que incluye este nuevo iOS: el Social Framework.
Este framework reemplaza al de Twitter y se hace más generico para ser compatible con las redes sociales de Facebook y Sina Weibo (la de los chinos). Lo genial es que implementarlo en nuestras apps es sencillo, nosotros de momento vamos a publicar nuestro estado en Twitter y Facebook a través de la interfaz nativa del framework. Tiene otras aplicaciones como mostrar el timeline y hacer follows en Twitter y cosas así, pero estas las veremos más adelante ya que son un poco (y solo un poco) más complejas.
Una de las 2 clases que componen el Social Framework es SLComposeViewController, esta clase te permite mostrar una vista al usuario desde la cual el puede componer y publicar un mensaje en cualquiera de las redes sociales mencionadas arriba, incluyendo una imagen o URL con el mensaje.
Empezando con iOS 6 y el framework
Lo primero es tener Xcode 4.5 y el SDK de iOS 6 instalado, luego tener una cuenta de facebook y/o twitter configurada en el dispositivo.
Abrimos Xcode 4.5 y añadimos el Social Framework a nuestro proyecto. Para esto como ya sabemos nos vamos a la raiz del proyecto, seleccionamos nuestro target y en la pestaña Summary encontraremos el apartado Linked Frameworks and Libraries desde donde podemos agregarlo.
Acto seguido importamos en nuestro controlador la correspondiente cabecera y ya estamos listo para empezar a usarlo:
#import <Social/Social.h>
Diferencias con el framework de Twitter
Este nuevo framework es casi idéntico al anterior de Twitter, a grandes razgos la única diferencia que vamos a encontrar es en la forma de inicializar la clase, ya que en esta nueva tenemos que especificar el tipo de servicio al que nos estamos refiriendo, este servicio está definido en la propiedad serviceType y puede tener los siguientes valores:
SLServiceTypeFacebookSLServiceTypeTwitterSLServiceTypeSinaWeibo
Uno para cada red social, vamos. Por lo tanto, mientras en el de Twitter hacíamos una inicialización normal con alloc e init, en este nuevo le pasamos el servicio, de manera que nos queda así:
SLComposeViewController *twitter = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
Luego hay que hacer la comprobación de que el servicio es accesible y de si hay por lo menos una cuenta configurada para ese servicio en concreto en el dispositivo:
[SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]
Esto devuelve YES o NO dependiendo de la configuración que haya. Si nos devuelve YES vamos a mostrar la interfaz, de lo contrario mostraremos un error.
Mostrando la interfaz
Supongamos que todo ha ido bien hasta ahora en el código, pues ya estamos listos para mostrar al usuario la intefaz, así que primero vamos a dar algunos valores iniciales a los campos. Este paso si que es idéntico al del framework de Twitter ya que tiene las mismas propiedades con los mismos nombres:
// ponemos un texto inicial para la publicación [twitter setInitialText:self.tvPost.text]; // añadimos una imagen a nuestra publicación [twitter addImage:[UIImage imageNamed:@"logo_thxou.png"]]; // añadimos también una URL [twitter addURL:[NSURL URLWithString:@"http://www.thxou.com/2012/08/07/ios-enviar-correo-electronico-desde-la-aplicacion/"]]; // asignamos el completionHandler para manipular los resultados del envío [twitter setCompletionHandler:completionHandler]; // por último mostramos la interfaz de publicación [self presentViewController:twitter animated:YES completion:nil];
Tener muy en cuenta de asignar las propiedades antes de presentar la vista al usuario, de lo contrario os va a retornar un NO y la imagen o URL no se va a cargar junto con el mensaje. También os puede retornar NO si la imagen es demasiado grande o si hay demasiados caracteres en la URL, no obstante esto depende de las restricciones de que Twitter y Facebook ponen en las respectivas páginas oficiales de sus APIs.
Hecho todo esto presentamos al usuario la interfaz como lo haríamos con cualquier controlador.
Manipular los resultados
Lo único que nos queda por hacer es comunicarle al usuario si se ha enviado correctamente la publicación o no, para esto tenemos los resultados del envío devueltos en el argumento result de la propiedad completionHandler de la clase. Esta propiedad es un objeto de tipo bloque y es llamada justo antes de cerrase la interfaz de envío. Ya que el bloque es lo último que se va a ejecutar, tenemos que cerrar la interfaz dentro de este bloque:
// manipulamos los mensajes de error si hay usando la propiedad completionHandler
SLComposeViewControllerCompletionHandler completionHandler = ^(SLComposeViewControllerResult result)
{
switch (result) {
case SLComposeViewControllerResultCancelled:
NSLog(@"La publicación ha sido cancelada.");
break;
case SLComposeViewControllerResultDone:
NSLog(@"Se ha publicado satisfactoriamente.");
break;
default:
break;
}
[self dismissViewControllerAnimated:YES completion:nil];
};
Conclusión
Como hemos podido observar es realmente sencillo mostrar la interfaz de envío por defecto al usuario. No obstante esto tiene sus limitaciones evidentes, como la de querer saltarse la interfaz y enviar directamente el mensaje a Twitter. En el siguiente tutorial veremos como hacer todo esto y alguna cosa más, es un poco más complejo, pero como veréis el framework lo hace todo mucho más sencillo y hace mucho trabajo por nosotros.
Parsear y crear ficheros en formato JSON en iOS
- 11 septiembre 2012
- iOS, Objective-C, Programación para iOS, Tutoriales
- 3 comments
Desde la salida de iOS 5 Apple incluyó en su API la clase NSJSONSerialization, la cual nos permite convertir objetos JSON en objetos de Objective-C (que ya tocaba también) y viceversa de manera sencilla.
Para los que no saben que es JSON (JavaScript Object Notation), es un tipo de sintaxis que nos permite representar porciones grandes o pequeñas de datos para poder almacenarlos y/o intercambiarlos con otros entornos. Mucho más sencillo y pequeño que XML y además más rápido de parsear. Usa exactamente la misma sintaxis que usa JavaScript para crear objetos pero es totalmente independiente del lenguaje. Esta y otras cosas son las cosas que lo hacen atractivo para empresas como Twitter, Yahoo, Google, etc., que ya tienen sus APIs adaptadas a JSON desde hace un tiempo, además que facilita las cosas al programador y ahora veremos hasta que punto.
La sintaxis de JSON
Es muy sencillo aprender la notación de JSON ya que tan solo hay 3 cosas para considerar:
- Los datos son pares de nombres y valores separados por comas.
- Los objetos están encerrados entre llaves ({ }).
- Los arrays están encerrados entre corchetes ([ ]).
Esto se cumple para todo en el fichero, también para los objetos que van anidados dentro de los arrays. No obtante solo se permiten cierto tipo de objetos, y son: NSString, NSNumber, NSArray, NSDictionary o NSNull, de manera que si quieres meter objetos como por ejemplo NSDate, vas a tener que convertirlos a NSStringantes o algún objeto compatible. Un fichero de JSON luce así por dentro:
{
"nombre" : "ThXou",
"web" : "thxou.com"
"aficiones" : [
{ "titulo" : "Fútbol" },
{ "titulo" : "Snowboard" }
]
}
Aquí podemos identificar que todo eso es un objeto (va encerrado entre { }) y luego tiene varios datos, entre ellos un array (aficiones) que contiene a su vez 2 objetos con un dato por objeto (título). Se cumplen los 3 puntos de arriba en todos los casos, esto es algo a considerar siempre. También hay que considerar que el nivel más alto del fichero JSON debe ser o un NSDictionary o un NSArraypara que este sea válido.
Parseando JSON desde una URL
Nuestra información en JSON está alojada en un sitio web, así que tenemos primero que descargar esta información y convertirla en un objeto manipulable, por eso usamos el método dataWithContentsOfURL:options:error: y hacemos la conversión a NSData:
- (void)viewDidLoad
{
[super viewDidLoad];
// creamos un botón para generar el JSON
UIBarButtonItem *json = [[UIBarButtonItem alloc] initWithTitle:@"Generar JSON"
style:UIBarButtonItemStylePlain
target:self
action:@selector(generarJSON:)];
self.navigationItem.rightBarButtonItem = json;
[json release];
// indicamos la url desde donde tomaremos los datos
NSURL *url = [NSURL URLWithString:@"http://dl.dropbox.com/u/270074/iOSTutorials/JSON/test.json"];
NSData *urlData = [NSData dataWithContentsOfURL:url];
[self empezarAParsear:urlData];
}
Simplemente usamos el método viewDidLoad: para descargar esta información al inicio de la carga del controlador. Hay que saber que la descarga de información en el hilo principal (Main thread) podría bloquear la interfaz gráfica, a nosotros no nos sucede debido a que descargamos muy poca información. Si vas a descargar grandes cantidades de datos es necesario hacerlo en segundo plano.
Antes de iniciar la descarga creamos un botón que más adelante nos va a permitir generar datos en formato JSON a partir de lo que ya tenemos parseado. Luego de la descarga, toda la información va a estar contenida en un objeto NSData. La clase NSJSONSerialization tiene el método JSONObjectWithData:options:error:, el cual nos permite parsear el contenido de este objeto.
Como hemos dicho antes el nivel más alto del ficher0 en JSON debe ser un diccionario o un array, en nuestro caso es un diccionario con una única key llamada comunidades; esta a su vez es un array con otros objetos dentro. Por lo tanto vamos a parsear la información directamente a un objeto NSDictionary:
- (void)empezarAParsear:(NSData *)urlData
{
NSError *error = nil;
// parseamos los datos de la URL
NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:urlData
options:kNilOptions
error:&error];
// si hubo algún error en el parseo lo mostramos
if (error != nil)
{
NSLog(@"ERROR: %@", [error localizedDescription]);
}
else {
self.comunidades = [jsonDic objectForKey:@"comunidades"];
}
}
La propiedad comunidades almacena el array contenido en el fichero bajo la key comunidades. El paso que queda ahora es mostralo. Como puede observar, uno de los argumentos del método JSONObjectWithData: es options:. Este argumento puede tener 3 valores diferentes según la configuración que se quiera tener en cuenta al momento de parsear el fichero. Yo he puesto kNilOptionsque equivale a 0, o a no elegir ninguna opción más concretamente, pero tu podría elegir entre estos 3:
NSJSONReadingMutableContainers:Los arrays y diccionarios son creados como objetos mutables, es decir que pueden ser cambiados en cualquier momento, incluso antes de comenzar el parseo.NSJSONReadingMutableLeaves:Todos los strings contenidos serán creados como mutables.NSJSONReadingAllowFragments:Permite el parseo de objetos en el nivel más alto del fichero pero no sean arrays o diccionarios.
Mostrando el contenido
Para que se vea más claro he usado un TableView para mostrar los datos parseados. Primero obtenemos la información de cada comunidad en el diccionario comunidad y segundo la vamos mostrando formateada a medida que se van recorriendo las filas de la TableView:
NSDictionary *comunidad = [self.comunidades objectAtIndex:indexPath.row];
cell.textLabel.text = [comunidad objectForKey:@"nombre"];
NSString *subtitle = [NSString stringWithFormat:@"Superficie: %@ - %@%%",
[comunidad objectForKey:@"superficie"],
[comunidad objectForKey:@"porcentaje"]];
cell.detailTextLabel.text = subtitle;
Todo esto lo hacemos en el método tableView:cellForRowAtIndexPath: el cual va ser el encargado de llenar los campos del TableView con la información correspondiente.

Generando datos en formato JSON
Ahora haremos el proceso inverso. Ahora convertiremos datos de un array a datos en formato JSON. Para hacer esto hay un botón en la barra de navegación del TableView que creamos en el ViewDidLoad:, y que al ser presionado ejecuta el método generarJSON::
- (void)generarJSON:(id)sender
{
NSError *error = nil;
// generamos los datos en JSON
NSData *json = [NSJSONSerialization dataWithJSONObject:self.comunidades
options:NSJSONWritingPrettyPrinted
error:&error];
// convertimos los datos a un string para poder mostrarlos
NSString *jsonString = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];
// los mostramos en un alertView
UIAlertView *alerta = [[UIAlertView alloc] initWithTitle:nil
message:jsonString
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alerta show];
[alerta release];
[jsonString release];
}
Para no hacer mucho más largo este tutorial simplemente hemos convertido toda la información que ya estaba parseada nuevamente a formato JSON, esto lo hemos hecho con el método dataWithJSONObject:options:error. Este método también tiene un argumento options:, pero en este caso solo tiene una opción:
NSJSONWritingPrettyPrinted:Con esta opción se hace la conversión pero la devuelve en un formato más legible y ordenado (tal y como verás al presionar el botón). Si por el contrario deseas compactar más el string devuelto (sin espacios ni tabuladores), puedes pasarle la opciónkNilOptions.
Luego de hacer la conversión mostramos los datos en una ventana de alerta.
Hay otro método que se puede usar para comprobar si es que la información que deseamos convertir a JSON se puede convertir o no. Este método es isValidJSONObject: y devuelve YES si es posible hacer la conversión y NO de lo contrario. Su uso es muy sencillo:
if ([NSJSONSerialization isValidJSONObject:self.comunidades])
{
// realizamos la conversión
}
else {
// mostramos un error
}
Conclusión
Es muy sencillo implementar esta funcionalidad como han podido ver. Tan solo hay un par de métodos que realmente usamos de esta clase, el resto son arreglos que nos permiten mostrar el contenido de acuerdo a nuestros gustos.
Recordar que no todos los formatos se pueden convertir a JSON, para los que no, hay que hacer otro tipo de conversiones. Un ejemplo es si vamos a pasar un objeto NSDate, podemos usar el método stringFromDate: de la clase NSDateFormatter para convertirlo a un string, y luego volver a recuperarlo con el método dateFromString:.
Esto es todo. Cualquier duda no dudéis en usar los comentarios.
Aprender a usar URL Schemes en iOS – URL personalizada
- 3 septiembre 2012
- iOS, Objective-C, Programación para iOS, Tutoriales
- One comment
Las URL Schemes permiten a las aplicaciones comunicarse, enviar y recibir mensajes y ejecutar acciones entre si a través de una URL personalizada. Pongamos un ejemplo, tenemos una aplicación que permite mostrar direcciones en el mapa, pues podríamos usar URL Schemes para que cualquier aplicación pudiera abrir direcciones en el mapa de nuestra aplicación.
Apple también tiene sus propias URL Schemes y seguramente las habrás visto en acción en páginas web que contienen números de teléfono y estos aparecen como enlaces, y cuando presionas en ellos se lanza la aplicación teléfono que hace una llamada a ese número, pues bien, aquí se utiliza la URL tel:. También están mailto: (para el correo), sms: y peticiones con http para los mapas y la aplicación de YouTube.
Registrando nuestra URL Scheme
Nosotros vamos a definir nuestra propia URL y para hacer esto hay 2 formas. La primera es a través del fichero info.plist. Como ya sabéis, este fichero esta compuesto por pares key-valor, así que añadimos una nueva key llamada CFBundleURLTypes (Al presionar enter aparecerá “URL types“). Veremos que es un array de diccionarios y solo contiene uno y dentro una key llamada “URL Identifier” (Su nombre original es CFBundleURLName), cuyo valor puede ser cualquiera, Apple recomienda que para que sea completamente único podríamos usar el de nuestro Bundle Identifier, en el caso de la app de ejemplo de este tutorial es: com.thxou.scheme.
Ahora agregamos a este diccionario una nueva key llamada CFBundleURLSchemes para definir el nombre de nuestra URL, al presionar enter su nombre cambiará a “URL Schemes“. En este array de strings vamos a definir nuestras URL (Porque puede haber más de una) poniendo el nombre con el que queremos que se abra nuestra app. Al poner el nombre hay que tener en cuenta 2 cosas:
- Cuidar que el nombre no sea uno de los ya definidos por Apple, ya que ellos dan prioridad a las aplicaciones nativas antes que a la tuya.
- Cuidar que el nombre no sea el de cualquier otra aplicación, el SO actualmente no sabe como reconocer cual es la aplicación a la que pertenece el URL Scheme, por lo tanto puede que ninguna App se abra.
El esquema final debe lucir más o menos así:
Si ahora mismo escribes scheme:// en Safari, se abrirá inmediatamente la aplicación.
La segunda forma es más fácil. Ir a la raíz del proyecto, seleccionar el target de nuestra aplicación, luego la pestaña Info y al final de todo veremos el apartado URL Types, desde aquí también podemos añadir nuevas URL Schemes presionando el botón de abajo llamado Add y seleccionando Add URL Type.

Recibiendo la URL
Como en principio se abrirá la aplicación y no hará nada, vamos a pasarle información en esa URL y a mostrarla en la vista principal. El encargado de manipular la URL que viene desde afuera es el delegado de la aplicación (el AppDelegate de toda la vida) y para hacer esto necesita implementar el método application:openURL:sourceApplication:annotation. La secuencia que sigue la aplicación cuando se le pide que abra una URL es la siguiente:

Este es el proceso cuando la aplicación no está abierta. Si está abierta la única posibilidad es que esté en background, así que en vez de la parte de inicialización, se ejecuta el método applicationWillEnterForegound:, esto se debe a que se necesita que la aplicación esté ejecutándose en primer plano, entonces la aplicación es pasada a este estado y luego es ejecutado el método application:openURL:sourceApplication:annotation.
Para no hacer las cosas más complicadas vamos a pasar simples textos a través de la URL, es solo para mostrar el funcionamiento de este sistema. La URL que vamos a ejecutar desde Safari va a ser: scheme://cualquiercosa/parametro1/parametro2. Ahora nos toca implementar el método correspondiente:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
// comprobamos que se ha recibido una url
if ([[url scheme] isEqualToString:@"scheme"])
{
// obtenemos el host de la url. En este caso será "cualquiercosa"
NSString *urlHost = [url host];
// obtenemos el path de la url por partes. No se cuenta el host
NSArray *urlComponents = [url pathComponents];
// componemos el string a mostrar
NSString *texto = [NSString stringWithFormat:@"Contenido de la URL:\n\nHost: %@\nParámetros:\n1. %@\n2. %@", urlHost, [urlComponents objectAtIndex:1], [urlComponents objectAtIndex:2]];
// lanzamos un alertView que nos muestre el string
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"URL Scheme"
message:texto
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
;
;
}
else {
// si no hay URL retornamos NO
return NO;
}
return YES;
}
Después de esto, desde cualquier aplicación pueden enviar información a tu aplicación que tu puedas manipular con solo ejecutar este código desde un botón:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"scheme://cualquiercosa/parametro1/parametro2"]];
No solo he querido explicar como trabajar con esto, sino también un poco de lo que ocurre detrás, últimamente me estoy aficionando a eso e igual me alargo un poco en los artículos, pero es necesario saber de que va la cosa para aprender bien. A por el próximo : ).
iOS: Enviar correo electrónico desde la aplicación
- 7 agosto 2012
- iOS, iPhone, Objective-C, Programación para iOS, Tutoriales
- 2 comments
Mientras trabajaba en mi aplicación Listed quise que los usuarios pudieran enviar sus listas por correo, y la verdad pensé que iba a ser difícil de hacer pero no fue así.
Apple provee una clase llamada MFMailComposeViewController dentro del framework MessageUI que hace muy sencillo el hecho de enviar correos sin tener que salir de la aplicación siquiera, esto, a través de la misma interfaz que usas en la aplicación Mail. Además te permite definir el contenido inicial de los campos del correo y también enviar uno o varios archivos adjuntos, como también provee de funcionalidad para responder a los eventos del usuario como enviar, cancelar o guardar un mensaje de correo.
La aplicación que toca esta vez va a permitir a los usuarios enviar un correo electrónico desde la aplicación. Usando el único método del delegado MFMailComposeViewControllerDelegate, vamos a mostrar al usuario mensajes dependiendo de si el correo se ha enviado, cancelado, guardado en borradores o ha fallado en el envío.
Preparando el terreno
Para este proyecto he creado uno de tipo Single View Application y le he puesto de nombre “emailMe“.
Lo primero que haremos será añadir el framework MessageUI a nuestra aplicación, luego vamos a definir un Outlet UILabel y una acción, así como también hacemos a ViewController conforme al protocolo MFMailComposeViewControllerDelegate. Para esto vamos al fichero ViewController.h y ponemos el siguiente código:
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
@interface ViewController : UIViewControler <MFMailComposeViewControllerDelegate>
{
UILabel *estado;
}
@property (nonatomic, retain) IBOutlet UILabel *estado;
- (IBAction)enviarCorreo:(id)sender;
@end
Acto seguido pasamos a la interfaz de usuario, así que abrimos el fichero ViewController.xib y añadimos simplemente 2 objetos, uno de tipo UIButton y otro UILabel. El botón es para abrir la ventana modal desde donde escribiremos el correo y el label es para mostrar el estado del envío. Una vez añadidos, conectamos el UILabel con el código. Hacemos esto presionando la tecla Ctrl y arrastrando el cursor desde el File’s Owner hasta el objeto UILabel en el Interface Builder y luego seleccionando el correspondiente de la lista que aparece.
Implementación de la funcionalidad
Hay un método al que siempre tenemos que llamar antes de mostrar la interfaz de envío de correo, y es: canSendMail, este nos va a servir para verificar si el dispositivo del usuario está habilitado para usar esta característica. En caso contrario, yo he decidido mostrar una alerta diciendo que el dispositivo no soporta el envío de correo.
Abrimos el ViewController.m para implementar nuestro método creado anteriormente:
@synthetize estado;
- (IBAction)enviarCorreo:(id)sender
{
// verificamos si es posible enviar correo desde este dispositivo
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *email = [[[MFMailComposeViewController alloc] init] autorelease];
email.mailComposeDelegate = self;
// definimos el asunto del correo
[email setSubject:@"Te envío una Web que mola"];
// definimos el contenido inicial del correo
NSMutableString *cuerpo = [[NSMutableString alloc] init];
[cuerpo appendString:@"Hey!, he encontrado esta web y me ha gustado : )<br /><br /><a href='http://www.thxou.com'>http://www.thxou.com</a>"];
// asignamos el cuerpo del mensaje y le decimos que lo interprete como HTML
[email setMessageBody:cuerpo isHTML:YES];
[cuerpo release];
// adjuntamos una imagen desde nuestro bundle
UIImage *imagen = [UIImage imageNamed:@"logo-thxou.png"];
NSData *imagenData = UIImagePNGRepresentation(imagen);
[email addAttachmentData:imagenData mimeType:@"image/png" fileName:@"thxoulogo"];
// mostramos la ventana modal con la interfaz de envío de correo
[self presentViewController:email animated:YES completion:nil];
}
else {
// mostramos una alerta si el dispositivo no puede enviar correo
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:NSLocalizedString(@"Tu dispositivo no soporta esta característica", nil)
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
;
;
}
}
Como veis es muy sencillo implementar esta funcionalidad en una aplicación, con lo poco que hemos escrito ya es posible enviar un correo, no obstante aún nos falta la parte de manipular los eventos que dije al principio.
El método setMessageBody:isHTML: como puedes observar te permite asignar un contenido inicial para el correo. Si deseas que este contenido sea interpretado como código HTML, lo haces a través etiqueta isHTML:, la cual te permite especificarlo.
Luego tenemos el método addAttachmentData:mimeType:fileName: que nos va a permitir enviar datos adjuntos al correo. El primer argumento requiere un objeto de tipo NSData, así que lo que sea que queramos enviar tiene que estar primero convertido a este tipo de datos, en mi caso el logo de la web en formato .png.
El segundo argumento (mimeType:) es el tipo MIME del archivo que vamos a enviar, esto no te lo tienes que inventar ya que es un stardard predefinido y puedes ver el tipo MIME correspondiente a tu archivo en http://www.iana.org/assignments/media-types/. El tercer argumento es simplemente el nombre que deseas que tenga el archivo cuando sea transferido a su destino. Puedes repetir este método para cada uno de los archivos que desees adjuntar.
Hay otros métodos como setToRecipients:, setCcRecipients: y setBccRecipients: que cumplen exactamente la misma función que en un correo ordinario.
Manipulando el resultado del envío
Como se puede ver en la línea 8 del código de arriba, asignamos como delegado a la clase ViewController, por lo tanto esta va a ser la encargada de cerrar la ventana desde donde enviamos el correo y manipular los resultados del envío, por lo tanto se espera que también implemente el método delegado mailComposeController:didFinishWithResult:error: de la clase MFMailComposeViewControllerDelegate. Nosotros si lo haremos:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
switch (result)
{
case MFMailComposeResultCancelled:
self.estado.text = @"La operación ha sido cancelada";
break;
case MFMailComposeResultSaved:
self.estado.text = @"El correo ha sido guardado en la carpeta borradores";
break;
case MFMailComposeResultSent:
self.estado.text = @"Correo puesto en la cola de envío satisfactoriamente";
break;
case MFMailComposeResultFailed:
self.estado.text = @"No se ha podido enviar o guardar el correo debido a un error";
break;
default:
break;
}
// cerramos la ventana modal de envío de correo
[controller dismissViewControllerAnimated:YES completion:nil];
}
Dependiendo de cual sea el evento, se devuelve un código de resultado diferente, esto nos permite personalizar la respuesta en cada caso.
Conclusión
Hoy hemos aprendido a enviar un mensaje de correo electrónico desde la misma aplicación. También hemos aprendido a mostrar la interfaz de envío con unos valores iniciales por defecto, así como a interceptar los mensajes de estado del envío del correo. Con respecto a esta clase, no hay mucho más que aprender, tan solo queda un par de cosas relacionadas con los mensajes de error en el método delegado, pero por lo demás, si haz comprendido bien este tutorial, ya puedes decir que dominas la clase MFMailComposeViewController
. No obstante para más detalle acerca de esta clase puedes pasarte por la documentación un momento, que siempre viene bien.
Espero que les haya gustado y servido. Cualquier duda, objeción o sugerencia la espero en los comentarios.








