thxou

Ama la sabiduría. Desea el conocimiento.

Trabajando Con El Social Framework: SLRequest, Publicar en Twitter Y Obtener El Timeline

| Comments

Seguint amb els tutorials sobre iOS 6, avui ens toca Twittear (o com s’escrigui) i mostrar el nostre timeline de Twitter en una aplicació.

Abans quan volíem comunicar-nos amb Twitter i obtenir dades i actualitzar-los calia fer una connexió a través de OAuth amb uns tokens i secret-keys, això normalment es podia digerir millor usant algun framework o classe externa que servia d’interfície de connexió entre el client i el servei. Des de l’arribada del framework de Twitter i la integració amb iOS 5 les coses es van posar extraordinàriament més fàcils, això ens va permetre l’intercanvi de dades amb Twitter en tan sol pocs passos.

En el Social *Framework tenim una classe anomenada SLRequest, molt similar a TWRequest del framework de twitter. Aquesta classe encapsula les propietats d’una petició HTTP en mètodes fàcils d’utilitzar, amb els quals enviem peticions a Twitter per poder obtenir i actualitzar dades dels nostres comptes configurats en el dispositiu.

Bàsicament enviem una petició HTTP amb uns paràmetres que configuren el que volem dur a terme en el servei, si Twitter diu que no hi ha problema, rebem una resposta amb unes dades que hem de manipular i mostrar a l’usuari, en cas contrari rebem una informació d’error.

Autenticant la petició

Com els deia paràgrafs enrere, abans calia usar tokens i secret-keys per autenticar-nos en Twitter i així poder validar les nostres peticions. Amb el Social Framework fem el mateix però de forma automàtica, més transparent a l’usuari, i en certa forma al desenvolupador, ja que en cap moment hem de manipular tokens.

Des de iOS 5 tenim el Accounts Framework, el qual proveeix un sistema centralitzat de comptes d’usuari. A través de l’es emmagatzemen tots els comptes de Twitter (I d’altres serveis) configurades en el dispositiu: informació d’usuari i contrasenya i altra, això ens permet saltar-nos aquesta típica finestra d’inici de sessió sense haver de preocupar-nos per proveir un sistema per emmagatzemar nosaltres mateixos les credencials.

Hi ha poques coses que podem fer sense autenticació d’usuari, i obtenir el timeline és una d’elles. No obstant això, per fer que això funcioni amb qualsevol compte, centralitzarem totes les peticions en els comptes obtinguts de la base de dades de comptes del dispositiu. Comencem:

El primer serà importar els frameworks:

1
2
#import <Accounts/Accounts.h>
#import <Social/Social.h>

Després fem la màgia d’obtenir els comptes del dispositiu amb el Accounts Framework:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ACAccountStore *accountStore = [[ACAccountStore alloc] init];

// creguem un objecte accountType especificant que solament volem obtenir els comptes de Twitter
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];

[accountStore requestAccessToAccountsWithType:accountType
                                      options:nil
                                   completion:^(BOOL granted, NSError *error)
{
    if (granted)
    {
        // guardem els comptes de twitter en un array 
        NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
        // armem la petició aquí
    }
    else {
        NSLog(@"Error no se pudo acceder a las cuentas: %@", [error localizedDescription]);
    }
}];

El que fem aquí és crear una instància de la base de dades de comptes. Després a través de la classe ACAccountType diem que solament volem els comptes de Twitter, passant-li la constant  ACAccountTypeIdentifierTwitter com a argument en la inicialització. Tot seguit demanem accés a les comptes amb el mètode requestAccessToAccountsWithType:options:completion:. Aquest mètode té com a argument el bloc completion:, el qual és un handler (Un objecte “manipulador” per així dir-ho) en els paràmetres del qual és retornada la resposta del mètode. Si tot va bé emmagatzemem tots els comptes obtinguts en el array accountsArray o vam mostrar un error en cas contrari. Simple.

Recorda aquesta dinàmica d’executar un mètode i rebre una resposta per ser manipulada en un bloc, perquè ho veuràs molt sovint des d’ara.

Construint la petició per obtenir el timeline

Amb el Social Framework és definitivament molt més fàcil construir una petició HTTP. Sabem que està composta per:

  1. Una URL que identifica l’operació que volem realitzar en el servei.
  2. Un mètode de petició, que pot ser GET, POST o DELETE.
  3. I uns paràmetres de configuració.

El mètode requestForServiceType:requestMethod:URL:parameters: passa totes aquestes dades com els seus arguments i això ens permet crear la petició en tan sol una línia de codi si així ho desitgem.

El que nosaltres volem és obtenir el timeline, per tant necessitem anar a la documentació oficial per veure el que hem d’usar. En entrar en l’enllaç i seleccionar l’operació de la qual volem veure els detalls (en aquest cas és: GET statuses/home_timeline), veurem en l’apartat Resourse Information certa informació molt important:

Tenim el mètode de la petició (GET), el format de resposta (JSON) i l’objecte de resposta (Tweets). Després està l’apartat Resource URL que és la URL que li passarem i l’apartat Parameters, que conté els paràmetres per configurar-la. Usant aquesta informació construïm la petició:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// guardem el compte
ACAccount *twitterAccount = [accountsArray objectAtIndex:0];
self.cuenta = twitterAccount;

// creguem la petició
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];

// associem el compte a la petició
[request setAccount:twitterAccount];

Per motius de brevetat tan solament utilitzarem el primer compte de les quals hi ha en el array.

He creat la propietat cuenta de tipus ACAccount, en ella emmagatzemem aquest compte per posteriorment poder enviar-la al controlador des del qual publicarem un missatge d’estat (el que és un Tweet) al nostre timeline, això més endavant.

Com poden veure tenim un diccionari per als paràmetres. El meu solament té una key: count, aquesta ens permet limitar la quantitat de tweets que ens va a retornar el timeline que per defecte és 20, però jo l’he posat a 25. En la documentació de la API estan tots els paràmetres que podem usar per configurar la petició.

El següent és assignar-li a la propietat account de la nostra petició, el compte que hem triat del array i amb la qual volem treballar per mostrar el timeline i altres coses.

Enviant la petició i manipulant els resultats

Una vegada construïda la petició procedim a enviar-la. Per a això usem el mètode performRequestWithHandler: que envia la petició i recull els resultats en el seu únic argument. Est és al seu torn un bloc, el qual és executat una vegada estan disponibles les dades de la resposta.

1
2
3
4
5
6
7
8
9
10
// realitzem la petició especificant un mètode per manipular la resposta
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
    if (responseData != nil)
    {
        self.tweets = [NSJSONSerialization JSONObjectWithData:responseData
                                                      options:kNilOptions
                                                        error:&error];
    }
}];

Aquest bloc té 3 paràmetres, el més important és responseData, perquè és el que va a contenir els tweets. Aquests tweets estan en format JSON com vam veure abans, per tant necessitem parsearlos i així passar-los a un format manipulable en Objective-C. Per fer això existeix la classe NSJSONSerialization, que agafa les dades en NSData (en aquest cas responseData), els parsea i retorna. Aquestes dades retornades els emmagatzemem en el array tweets, que a continuació usarem per mostrar-los a l’usuari.

Pots aprendre més sobre com parsear dades en format JSON i la classe NSJSONSerialization en el nostre pràctic tutorial sobre el tema fent clic aquí.

Mostrant els resultats

Bé, ja tenim fet gairebé tot el treball, ara solament ens queda mostrar els resultats, i para això tenim l’objecte tweetsTableView. A causa que ja tenim tots els Tweets en un array, és relativament senzill mostrar-los en el tableView, per això anem directament a implementar els mètodes convenients:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- (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"];

    // carreguem les imatges dels quals envien 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í hi ha algunes línies que els sonaran a xinès, no obstant això explicaré una mica per damunt que està succeint.

Al parsearse les dades aquests són emmagatzemats en el array tweets, però els tweets dins del, són emmagatzemats en forma de diccionaris, fàcilment recuperables usant la classe NSDictionary. Llavors recuperem cada camp d’aquest array en un diccionari que jo he anomenat tweet per fer-ho més identificable (en realitat això és el que representa). Com ja saben, accedim als valors d’un diccionari a través de keys, però quines són aquestes keys?, ens anem a la documentació oficial i ho mirem allí. El que fem és simplement mostrar el text del tweet com a títol i el nom “del que tweetea” com subtitulo en cada cel·la.

Per mostrar la imatge el que fem és usar el GCD (Grand Central Dispatch) d’Apple. A grans trets explicar-los que la descàrrega de dades de la xarxa sempre deuria ser de forma asíncrona, això és perquè és un procés que triga una mica a dur-se a terme i per tant no pot fer-se en el mateix thread (fil) ja que podem bloquejar-ho, i això deixaria inutilitzable la interfície d’usuari fins que es completi el procés, cosa que pel bé dels nostres usuaris, no volem. Doncs aquest problema ho soluciona el GCD, fent que certs mètodes s’executin de forma asíncrona (en un altre fil o thread), d’aquesta forma evitem bloquejar la interfície d’usuari.

Enviant un tweet

Doncs fer això és una mica més del mateix. Jo he creat un nou controlador per fer això anomenat EnviarTweetViewController, el qual es mostra en una finestra modal i té un TextView i dos botons: un per enviar el tweet i un altre per cancel·lar l’operació.

El d’enviar el tweet executa el mètode enviarTweet::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (IBAction)enviarTweet:(id)sender
{
    // comprovem si el camp per escriure el tweet no està buit
    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];

        // assignem el compte que usarem per publicar el tweet
        [request setAccount:self.cuenta];

         [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
         {
             NSDictionary *resultado = [NSJSONSerialization JSONObjectWithData:responseData
                                                                       options:kNilOptions
                                                                         error:&error];
         }];

        [self cancelar:nil];
    }
}

Com podeu observar es fa exactament el mateix. Tan solament canvien els paràmetres, la URL i el mètode de la petició, i tot això el podem trobar en la documentació oficial.

A diferència de l’anterior, aquí no accedim a tots els comptes del dispositiu, sinó que simplement passem a aquest controlador el compte que hem obtingut abans, així ens assegurem que el compte que està seleccionada és des de la qual s’envia el tweet i sobre la qual es fan les operacions sol·licitades. No hi ha misteri.

Conclusió

El procediment per dur a terme totes aquestes accions en Twitter porten la mateixa estructura. Tan solament varien els paràmetres, la URL i el mètode de la petició. Pel que, si volem fer qualsevol cosa,  hem d’anar a la documentació i mirar el que necessitem. Després reemplaçar les dades que hem vist abans amb els nous, enviar la petició i mostrar a l’usuari les dades obtingudes. Així de fàcil és treballar amb el Social Framework.

Ara pots passar-te per la documentació d’Apple sobre aquest tema i també visitar el nostre tutorial sobre com parsear i manipular fitxers JSON de forma nativa i així estendre una mica més els teus coneixements.

Comments