Objective-C e RESTful Web Services

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:
Marquei as opções Use Automatic Reference Counting e Include Unit Tests. Isso mesmo, optei pelos testes unitários:
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?
Filed under: Post | 4 Comments
Tags:Apple, Arquitetura, iOS








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.
Excelente post!
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/