Objective-C e RESTful Web Services

13maio12

fonte da imagem: http://swx.com.br

Você já tentou acessar Web Services via Objective-C? Usou bibliotecas de terceiros? Você sabia que o próprio Foundation Framework já traz tudo que você precisa? Resolvi escrever este post para mostrar como fiz.

Para fazer os testes, utilizei o EncomendaZ RESTful Web Services que eu mesmo fiz. Se quiser uma explanação rápida sobre a descrição do serviço de monitoramento que utilizei neste post, acesse aqui.

O primeiro passo foi criar o projeto. Poderia ser uma aplicação, mas criei uma biblioteca. Não é objetivo deste post implementar as telas, vamos focar no consumo dos serviços. Optei pela biblioteca estática:

Cirando um novo projeto.

Marquei as opções Use Automatic Reference Counting e Include Unit Tests. Isso mesmo, optei pelos testes unitários:

Selecionando as opções do novo projeto

Criei a classe Monitoramento que representa objeto monitorado, associado a um determinado e-mail:

//  Monitoramento.h

#import <Foundation/Foundation.h>

@interface Monitoramento : NSObject

// Código dos Correios referente ao objeto monitorado
@property NSString* objeto;

// E-mail que será notificado a cada mudança no status do objeto
@property NSString* email;

@end

E a sua respectiva implementação:

//  Monitoramento.m

#import "Monitoramento.h"

@implementation Monitoramento

@synthesize objeto;
@synthesize email;

@end

Método PUT

Criei a classe MonitoramentoManager com a responsabilidade de centralizar todos os acessos aos serviços. Comecei pela definição da operação de inserção:

//  MonitoramentoManager.h

#import <Foundation/Foundation.h>
#import "Monitoramento.h"

@interface MonitoramentoManager : NSObject

+ (BOOL)inserir:(Monitoramento *)monitoramento;

@end

Criei a classe que representa o caso de teste:

//  MonitoramentoManagerTest.h

#import <SenTestingKit/SenTestingKit.h>

@interface MonitoramentoManagerTest : SenTestCase

@end

Implementei o teste convencionando que as operações bem sucedidas retornarão verdade:

//  MonitoramentoManagerTest.m

#import "MonitoramentoManagerTest.h"
#import "MonitoramentoManager.h"

@implementation MonitoramentoManagerTest

- (void) testInserirMonitoramentoComSucesso
{
    Monitoramento *monitoramento = [[Monitoramento alloc] init];
    monitoramento.objeto = @"PB882615209BR";
    monitoramento.email = @"cleverson.sacramento@gmail.com";

    BOOL sucesso = [MonitoramentoManager inserir:monitoramento];

    STAssertTrue(sucesso, @"O resultado tem que ser verdade");
}

@end

Implementei o método estático inserir:

//  MonitoramentoManager.m

#import "MonitoramentoManager.h"

@implementation MonitoramentoManager

+ (BOOL)inserir:(Monitoramento *)monitoramento
{
    // URL do serviço de monitoramento no ambiente de testes (sandbox).
    NSURL *url = [NSURL URLWithString:@"http://services.sandbox.encomendaz.net/monitoring.json"];

    // Criando a requisição com o método PUT.
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"PUT"];

    // Usando o PUT devemos passar os parâmetros via body exigidos pelo serviço (clientId e trackId).
    NSString *body = [NSString stringWithFormat:@"clientId=%@&trackId=%@", monitoramento.email, monitoramento.objeto];
    [request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];

    // Como o serviço exige que os parâmetros sejam passados via FORM...
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

    // Submetendo a requisição e obtendo o resultado.
    NSError *error;
    NSData *resultado = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];

    // Verificando a ocorrência de erros HTTP.
    if(error){
        NSLog(@"Erro HTTP: %@", error.description);
        return NO;
    }

    // O serviço retorna JSON com o resultado da operação, mas, por enquanto, vou ignorar para facilitar o entedimento.
    NSLog(@"Resultado: %@", [[NSString alloc] initWithData:resultado encoding:NSUTF8StringEncoding]);

    return YES;
}

@end

Método GET

Para consultar os monitoramentos associados a um determinado e-mail, criei o seguinte método no manager:

//  MonitoramentoManager.h

#import <Foundation/Foundation.h>
#import "Monitoramento.h"

@interface MonitoramentoManager : NSObject

...

+ (NSArray *)obterPorEmail:(NSString *)email;

@end

Implementei o caso de teste escrevendo no console do Xcode os dados retornados pelo manager:

//  MonitoramentoManagerTest.m

#import "MonitoramentoManagerTest.h"
#import "MonitoramentoManager.h"

@implementation MonitoramentoManagerTest

...

- (void)testObterMonitoramentosComSucesso
{
    NSArray *monitoramentos = [MonitoramentoManager obterPorEmail:@"cleverson.sacramento@gmail.com"];

    for (Monitoramento *monitoramento in monitoramentos) {
        NSLog(@"objeto: %@\n", monitoramento.objeto);
        NSLog(@"email : %@\n", monitoramento.email);
    }

    STAssertTrue(monitoramentos.count > 0, @"É preciso ter monitoramentos cadastrados");
}

@end

Fiz o acesso ao serviço para obter os dados:

//  MonitoramentoManager.m

#import "MonitoramentoManager.h"

@implementation MonitoramentoManager

...

+ (NSArray *)obterPorEmail:(NSString *)email
{
    // URL do serviço de monitoramento no ambiente de testes (sandbox).
    NSString *stringUrl = [NSString stringWithFormat:@"http://services.sandbox.encomendaz.net/monitoring.json?clientId=%@", email];
    NSURL *url = [NSURL URLWithString:stringUrl];

    // Submetendo a requisição sem o NSMutableURLRequest, assim vai com GET.
    NSError *error;
    NSData *resultado = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];

    // Verificando a ocorrência de erros HTTP.
    if(error){
        NSLog(@"Erro HTTP: %@", error.description);
        return nil;
    }

    // Acessando o elemento "data" da estrutura retornada pelo serviço.
    NSDictionary *respostaJSON = [NSJSONSerialization JSONObjectWithData:resultado options:kNilOptions error:nil];
    NSArray *monitoramentosJSON = [respostaJSON objectForKey:@"data"];

    Monitoramento *monitoramento;
    NSMutableArray *monitoramentos = [[NSMutableArray alloc] init];

    // Obtendo cada um dos monitoramentos retornados e preenchendo o array.
    for(NSDictionary *monitoramentoJSON in monitoramentosJSON) {
        monitoramento = [[Monitoramento alloc] init];

        monitoramento.objeto = [monitoramentoJSON objectForKey:@"trackId"];
        monitoramento.email = [monitoramentoJSON objectForKey:@"clientId"];

        [monitoramentos addObject:monitoramento];
    }

    return monitoramentos;
}

@end

Método DELETE

Para excluir os monitoramentos, defini o método estático excluir no manager:

//  MonitoramentoManager.h

#import <Foundation/Foundation.h>
#import "Monitoramento.h"

@interface MonitoramentoManager : NSObject

...

+ (BOOL)excluir:(Monitoramento *)monitoramento;

@end

Implementei o teste:

//  MonitoramentoManagerTest.m

#import "MonitoramentoManagerTest.h"
#import "MonitoramentoManager.h"

@implementation MonitoramentoManagerTest

...

- (void)testExcluirMonitoramentoComSucesso
{
    Monitoramento *monitoramento = [[Monitoramento alloc] init];
    monitoramento.objeto = @"PB882615209BR";
    monitoramento.email = @"cleverson.sacramento@gmail.com";

    BOOL sucesso = [MonitoramentoManager excluir:monitoramento];

    STAssertTrue(sucesso, @"O resultado tem que ser verdade");
}

@end

E parti para a implementação do manager:

//  MonitoramentoManager.m

#import "MonitoramentoManager.h"

@implementation MonitoramentoManager

...

+ (BOOL)excluir:(Monitoramento *)monitoramento
{
    // URL do serviço de monitoramento no ambiente de testes (sandbox).
    NSString *stringUrl = [NSString stringWithFormat:@"http://services.sandbox.encomendaz.net/monitoring.json?clientId=%@&trackId=%@", monitoramento.email, monitoramento.objeto];
    NSURL *url = [NSURL URLWithString:stringUrl];

    // Criando a requisição com o método DELETE.
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"DELETE"];

    // Submetendo a requisição e obtendo o resultado.
    NSError *error;
    NSData *resultado = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];

    // Verificando a ocorrência de erros HTTP.
    if(error){
        NSLog(@"Erro HTTP: %@", error.description);
        return NO;
    }

    // O serviço retorna JSON com o resultado da operação, mas, por enquanto, vou ignorar para facilitar o entedimento.
    NSLog(@"Resultado: %@", [[NSString alloc] initWithData:resultado encoding:NSUTF8StringEncoding]);

    return YES;
}

@end

Finalizando…

Eu sei, eu sei… Os casos de teste não podem depender uns dos outros. Sei também que existe código replicado e que pode ser melhorado. Sei que o retorno dos serviços não estão sendo devidamente tratados. Fiz de propósito para não perder o foco, mas o código-fonte está no Github: https://github.com/zyc/lab/tree/master/ios/rest.

Agora é por sua conta, invoque o manager diretamente dos seus view controllers. Se quiser dar uma relembrada nestes modelos arquiteturais, sugiro a leitura do post MVC ou Arquitetura em Camadas?

Anúncios


5 Responses to “Objective-C e RESTful Web Services”

  1. 1 rafikdabahia

    Ficou muito bom o post! Os testes que fiz foi utilizando RestKit por conta de precisar manipular estruturas complexas de objetivos de entrada e saída para cenários mais simplificados, como é o caso do serviço dos correios este formato que você apresentou se torna mais simples e efetivo.

  2. Excelente post!

  3. Segue abaixo um post sobre como realizar testes no web service dos correios usando a ferramente SoapUi
    http://regifelix.com/2013/01/06/testes-de-web-services-com-a-ferramenta-soapui/

  4. 4 Kleber Cardoso

    Otimo post, mas seria possivel voce completar os metodos para inserir e deletar? realmente, nao estou encontrando material na net sobre isto… agradeco.


  1. 1 Minicurso iOS Dev no LinguÁgil 2012 « Cleverson Sacramento

E aí, o que você achou? Comenta aí...

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s