Resultados para 'iphone sdk'

Testes no iPhone - Usando o framework padrão do Xcode

Escrito por Afonso Junior em 02/03/10 11:30

O post do Felipe sobre testes em iPhone, escrito em janeiro do ano passado, serviu como parâmetro para vários projetos nossos aqui na Mobits. Só depois de um bom tempo utilizando o framework do Google, descobrimos lendo posts do Matt Gallagher no Cocoa with love que o framework nativo do Xcode estava disponível para projetos em iPhone.

Pesquisando na internet, decidimos implementar um projeto utilizando o framework padrão. Este post visa mostrar como configurar seu projeto para iPhone usando o framework nativo e compará-lo com o GTM. Por enquanto, estamos lidando apenas com teste unitários. Em um post futuro comentamos sobre Mock e como adicioná-lo ao seu projeto.

Configuração

Uma vez criado o projeto, você deve criar um novo target para o projeto. Clique com o botão direito em Target > Add > New Target....

Adicionando novo target

Como template, escolha Unit Test Bundle. Escolha um nome para o target (sugestão: Testes Unitarios) e clique em Finish.

Agora, arraste o target original para dentro do target de teste, para criar dependência (forçar o aplicativo a dar build no projeto antes de executar os testes). Após fazer isso, o target deve ficar assim:

Targets aninhados

Pronto! Com isso você já tem o suficiente para rodar seus testes. Mas pode ser que você queira algo a mais, então vamos ao próximo passo que pode ser muito útil nos seus testes.

Debug

Esta foi a parte mais complicada de fazer funcionar. Depois de muitas indas e vindas em diversos sites, conseguimos configurar o debug (funcionando com breakpoint). Assim você pode correr passo-a-passo as linhas de código dos seus testes caso precise. Vale ressaltar que o debug não funciona caso tenha ocorrido erro de compilação, mas funciona perfeitamente em caso de falhas ocorridas nos testes.

Mas vamos ao que interessa: primeiro, crie um executável para o projeto (Project > New Custom Executable...).

Criando executável

Na janela que se abre, escolha o nome do executável. Ele deverá executar o otest, um programa de termnal que serve para depurar o código das classes em Objective-C. Ele deve ser específico para o SDK do iPhone que você utiliza no projeto. Para saber onde ele está e qual versão você deve utilizar, vá no terminal e digite:

find /Developer -name otest

Eu obtive os seguintes resultados:

/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.0.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.2.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/Developer/usr/bin/otest
/Developer/Tools/otest

Que fique bem claro que o último não serve para projetos em iPhone. No nosso caso, usamos o segundo (3.1.2). Clique em Finish. Agora precisamos configurar os argumentos do executável na janela que aparece. A imagem abaixo mostra como eles devem ser configurados:

Parâmetros do executável

Na seção dos argumentos, você deve obedecer a ordem em que eles estão escritos. O segundo argumento deve ser o nome do produto gerado pelo target de testes que você utiliza (veja no grupo Products). Na seção de variáveis, utilize os seguintes valores:

Variável Valor
DYLD_LIBRARY_PATH ${BUILD_PRODUCTS_DIR}:${DYLD_LIBRARY_PATH}
DYLD_FRAMEWORK_PATH "${BUILD_PRODUCTS_DIR}:${DEVELOPER_LIBRARY_DIR}/Frameworks:
${DYLD_FRAMEWORK_PATH}"
DYLD_NEW_LOCAL_SHARED_REGIONS YES
CFFIXED_USER_HOME "${HOME}/Library/Application Support/iPhone Simulator/User"
IPHONE_SIMULATOR_ROOT $SDKROOT
DYLD_NO_FIX_PREBINDING YES
DYLD_ROOT_PATH /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/
iPhoneSimulator3.1.2.sdk

Cuidado com a última variável: ela deve ser a mesma versão do SDK utilizado com o otest. Feito isso, você já possui a configuração necessária para "debugar" seus testes. Tudo que você precisa fazer é configurar o executável como o padrão (Project > Set Active Executable). Marque os breakpoints que achar necessário e execute com "Build and Debug".

Comparações

Com o framework configurado e com vários casos de teste já implementados, fica inevitável a comparação entre o framework padrão e o GTM. Algumas diferenças são notadas logo de cara.

  • Menos métodos de teste: foi um susto ao ver que o framework padrão não tinha o STAssertEqualStrings. Mas nada que assustasse muito: o STAssertEqualObjects servia para o mesmo propósito. No final das contas, não houve nenhum teste que não conseguimos fazer por não existir método para tal.

  • Bundle: Isso é ponto positivo para o GTM. Para acessar arquivos no ambiente padrão você não utiliza o [NSBundle mainBundle]. Para fazer funcionar, tivemos que configurar no target (na aba Properties, no campo Identifier) um identificador que pudesse ser referenciado no código. Se, por exemplo, você colocar lá um identificador "com.mobits.caricas.tests", basta chamar o bundle identificado por essa string: [NSBundle bundleWithIdentifier:@"com.mobits.caricas.tests"]. É óbvio que isso nos causou alguns problemas nos testes, principalmente nos casos em que no target do aplicativo eu tinha que rodar em um bundle e no target de testes eu tinha que rodar em outro. A solução foi utilizar diretivas de pré-compilação.

  • Build Results: nisso o framework padrão ganha de lavada do GTM. Enquanto neste os erros aparecem todos duplicados, e listados diretamente na lista dos resultados do build, naquele aparece bem organizado, cada erro apontado hierarquicamente dentro do seu específico caso de teste.

Exibição de erros no GTM
Exibição dos erros no GTM: duplicado

Exibição dos erros no framework padrão: visão hierárquica dos testes
Exibição dos erros no framework padrão: visão hierárquica dos testes

Conclusão

Por ser mais simples de configurar, por exibir melhor os erros dos testes e por não ter certas mandingas que o GTM tem (não poder rodar os testes com o iPhone Simulator aberto, por exemplo), a escolha é o framework padrão do Xcode. Os métodos a mais de teste do GTM não foram um diferencial tão grande que pesasse em favor dele, mas talvez aqueles que o utilizam e desejam migrar vão ter alguns problemas de adaptação, mas nada que seja o fim do mundo.

Em um post futuro eu comento outra parte importante nos testes, a técnica de Mock Objects, como configurá-lo e como utilizá-lo em seus testes. Ah, e se você encontrou algum erro ao tentar configurar o ambiente, adiciona um comentário aqui, pode ser que já tenhamos passado por isso e saibamos como solucionar.

Grande abraço!

Leia também

Testes de interface no iPhone SDK com o UIRecorder

Escrito por Quintana em 15/01/10 12:30

Muitos desenvolvedores ainda não conhecem o grande poder da ferramenta Instruments, uma das três disponíveis no Kit de desenvolvimento do iPhone. Recentemente eu descobri que ela além de medir o desempenho de aplicações, uso de memória e tudo mais, também pode nos auxiliar nos testes de interface.

Já mostramos aqui, no nosso blog, a ferramenta open source Bromine, desenvolvida por nosso colaborador Felipe Barreto, com auxílio de Matt Gallagher. Com o Bromine é possível criar testes que simulam a interação com diversos elementos do iPhone (como tabelas, botões, etc) e verificar o resultado dessas interações. Contudo, como é possível testar interfaces mais complexas, como a de jogos, onde não existem botões, tabelas ou labels? Para isso, temos que ir no simulador e fazer os testes na mão mesmo. E se esses testes forem complicados de se reproduzir? Então usamos o UIRecorder!

O UIRecorder nada mais é do que um gravador e reprodutor das interações que você faz no iPhone Simulator. Primeiro, você deve abrir o iPhone Simulator e o Instruments e escolher o UIRecorder:

UIRecorder

Dentro do UIRecorder, vá em "Attach to Process" e escolha o processo do iPhone Simulator. Com isso ele ficará vinculado ao iPhone Simulator e estará pronto para salvar as suas interações. Abaixo, existe um vídeo mostrando o funcionamento da ferramenta:

Um bom exemplo é o aplicativo Mobits Button Soccer, onde a interação com os botões é complicada de simular com outras ferramentas e de reproduzir. Com o UIRecorder eu posso gravar os testes, modificar o código e depois reproduzí-lo para ver se tudo funciona.

A única desvantagem do UIRecorder é que ele não é capaz de determinar de forma automatizada se o software está funcionando ou não, como o Bromine faz, mas já é uma mão na roda para quem quer reproduzir interações complexas e onde o custo de ficar olhando se elas estão corretas é pequeno.

Leia também:

Desenhando fragmentos de interface no Interface Builder

Escrito por Felipe Barreto em 08/01/10 14:52

Quem já começou a desenvolver suas apps para iPhone deve ter passado pelo processo padrão de criar interfaces no Interface Builder: cria o arquivo XIB, vincula-o à view de algum controller e carrega este último com o método - [UIViewController initWithNibName:(NSString *)n bundle:(NSBundle *)b].

Contudo, podem existir casos onde você queira definir apenas um pedaço da interface para ser utilizado diversas vezes por um mesmo controller ou por diversos controllers diferentes. Nestes casos, aparece um novo método:

// Em NSBundle
- (NSArray *)loadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options

Usado da maneira mais simples, este método retornará um array das views definidas na raiz de um determinado XIB.

Vamos ao exemplo.

Exemplo de Arquivo XIB

Ao carregar o XIB acima com o seguinte código:

NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"Fragmentos" owner:nil options:nil];
UIView *cabecalho = [views objectAtIndex:0];
UIView *rodape = [views objectAtIndex:1];

o array obtido conterá somente as views Cabecalho e Rodape, pois são as que estão no nível mais alto. As outras views serão naturalmente carregadas, pois são subviews das principais.

Depois de incluir suas novas views na sua tela, o resultado final poderá ser algo como:

Tela com fragmentos de interface

Você ainda pode definir um objeto de uma classe que você tenha criado como owner do XIB. Desta maneira, poderá utilizar os recursos de ligação dinâmica que o Interface Builder fornece para ligar os elementos visuais às propriedades IBOutlet da sua classe. Para isso, basta definir a classe do seu objeto no File's Owner do arquivo XIB, configurar as ligações e carregá-lo com o comando NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"Fragmentos" owner:meuObjeto options:nil];

Desempenho

Apesar de facilitar muito no desenho de interfaces complexas, o carregamento dos XIB é naturalmente mais lento que a criação de views via código puro, portanto, em situações onde o XIB tenha de ser carregado diversas vezes em um curto espaço de tempo, deve-se avaliar o impacto sobre a performance. Um caso comum seria criar um XIB para definir a aparência da célula de uma tabela. Eu venho utilizando esse recurso constantemente nos novos projetos, mas tenho que ficar atento a tudo que posso fazer para compensar o carregamento lento como, principalmente, o reaproveitamento de células já carregadas.

Como falei acima, venho utilizando este recurso sempre que possível nos projetos e tenho aumentado muito a produtividade na criação de interfaces. Se tiver alguma dúvida sobre o funcionamento, fique a vontade para pedir ajuda.

Leia também:

UIPickerView circular - "Gambiarra oficial" da Apple

Escrito por Felipe Barreto em 30/11/09 12:44

Para quem não conhece, o UIPickerView é o componente do iPhone que parece um letreiro de máquinas caça-níquel e é normalmente utilizado em situações onde você tem que escolher uma dentre várias opções.

UIPickerView normal

Não vou me aprofundar sobre o uso deste componente em situações normais, pois já existe documentação suficiente para isso, mas pretendo explorar uma maneira de estender seu comportamento, permitindo que a lista de opções apareça de uma maneira circular, num loop infinito.

O componente padrão, infelizmente, não possui qualquer mecanismo para viabilizar essa funcionalidade, contudo existe um outro componente, UIDatePicker, que utiliza o UIPickerView com uma lista circular para escolher dias, minutos, etc. Vendo isso, conclui: "bom, tem um jeito.. a Apple só não quer nos contar qual".

UIDatePicker com lista circular

Procurando o truque

Coloquei o UIDatePicker numa app de teste para rodar no iPhone e ativei o modo de debug para começar a destrinchar o componente. Fiz um passeio entre dezenas de views e subviews, trocando ponteiros em tempo de execução, chamando métodos de objetos que nunca teria acesso de modo normal, entre outras coisas que crianças não devem tentar em casa :) e acabei topando com a solução para o problema. E me surpreendi!

Expondo a gambiarra

O UIPickerView sempre deve ser associado a um UIPickerViewDataSouce e a um UIPickerViewDelegate que vão prover as informações necessárias para montar o componente.

Vou assumir que existe um array que será a base de dados para o componente.

NSArray *meusItens;

O UIPickerViewDataSouce possui um método que define o número de linhas (rows) que cada subcomponente do UIPickerView deverá exibir. É aí que começa o truque.

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return [meusItens count] * 10000;
}

Observe que multipliquei o numero de elementos do array por 10.000!! Sim! Este é o macete que a Apple usa para fazer uma lista "infinita". Não tem nada de infinita, mas sim uma lista suficientemente grande para que somente os usuário mais chatos curiosos consigam chegar ao fim.

Para completar o truque, a lista de 10000 x N opções deve ser exibida a partir da metade. Desta maneira, ela terá scroll "infinito" em qualquer direção. Basta colocar a linha a seguir no viewDidLoad do seu controlador, ou onde achar melhor.

[minhaPickerView selectRow:([meusItens count]*5000) inComponent:0 animated:NO];

Repare que posicionei a minhaPickerView na posição 5000 x N, ou seja, na metade da lista.

Quando for necessário mapear uma linha qualquer dentro do seu array - como na hora de definir o conteúdo de cada linha ou decidir o que fazer quando o usuário escolher uma linha - basta usar o seguinte código.

[meusItens objectAtIndex:(row % [meusItens count])];

O que eu fiz foi aplicar o operador módulo (%) sobre o número da linha usando o tamanho da lista original. O resultado será sempre um inteiro entre 0 e o tamanho da lista menos 1 - que é o necessário para acessar todos os itens do array.

Pronto! Agora você tem uma lista circular com o carimbo da Apple :)

UIPickerView com lista circular

OBS: Utilize este recurso apenas se sua lista possuir um número razoável de elementos, pois se ela tiver até 4 ou 5, um ou mais elementos aparecerão duplicados na tela - o que ficaria bem esquisito.

Bom, é isto! Espero que tenha ficado claro, mas se restar dúvida, fique a vontade para colocá-la nos comentários.

Leia também:

  1. Desenhando interfaces para iPhone
  2. Saiba como customizar sua UITableView

Performance do SQLite Persistent Object

Escrito por Felipe Barreto em 18/09/09 11:46

Da última vez que comparei as soluções ORM para iPhone, cheguei a conclusão de que o Sqlite Persistent Object - SPO era o mais indicado para o projeto que estávamos desenvolvendo. Agora, com o projeto concluído, observamos que a performance não estava adequada às necessidades do cliente e começamos a buscar soluções.

Minha primeira idéia foi aproveitar a chegada do iPhone OS 3.0 e migrar do SPO para Core Data imaginando que, por ser uma solução desenvolvida especificamente para a plataforma, seria bem mais eficiente. Contudo, após estudo da API, vi que a mudança seria um pouco mais complicada, pois ela usa um paradigma diferente da anterior e eu teria que mexer muito além das classes de modelo, possivelmente mexendo em todas as outras classes, controladores principalmente. Embora tenha coberto as classes de modelo com testes unitários, achei que seria arriscado demais fazer a mudança estando tão próximo da entrada do sistema em produção. Decidi deixar essa idéia na gaveta e parti para tentar melhorar o que já estava implementado e em funcionamento.

Avaliando o tempo de cada atividade

Não faz sentido buscar melhorias na performance se você não tiver como saber se as modificações estão realmente influenciando no tempo de cada atividade. Para avaliar isso, existem algumas ferramentas disponíveis no Mac OS, como o Instruments e o Shark. Utilizando a segunda, consegui ter uma idéia clara de quais métodos estavam consumindo mais tempo da CPU através da análise de Time Profile.

Mas para medir o tempo de uma atividade completa - que atravessa vários métodos, threads, callbacks - eu desenvolvi uma classe simples, porém eficaz, para calcular esses tempos. Uma classe que batizei de Benchmark e que pode ser usada da seguinte maneira:

- baixarLista {
    [Benchmark start:@"baixarLista"];
    ....
}

- exibirLista {
    .....
    [Benchmark finish:@"baixarLista"];
}

A saída seria algo parecido com:

2009-09-17 13:52:49.729 MyAPP[876:20b] BENCHMARK: baixarLista started
2009-09-17 13:52:56.507 MyAPP[876:6b07] BENCHMARK: baixarLista: 6.777050s

As vantagens desta abordagem são:

  1. o código é pouco invasivo, pois você pode dar o import do Benchmark no .pch e usar em todas as classes;
  2. permite o uso de labels para identificar o benchmark, o que permite realizar vários benchmarks ao mesmo tempo sem se confundir;
  3. por utilizar somente métodos estáticos, permite que um benchmark comece num método de uma classe e termine no de outra classe sem que seja necessário o compartilhamento de dados entre as classes.

O código completo da classe você confere aqui.

Identificando os gargalos

Depois de levantar o tempo gasto e os métodos mais custosos durante a execução de cada atividade, pude me concentrar em encontrar os pontos considerados gargalos e que eu poderia fazer alguma mudança.

Datas

O SPO armazena os campos de data como strings no banco e todas as operações de recuperação ou gravação passam por métodos de conversão definidos em NSDate-SQLitePersistence.m. Analisando a saída do Shark, observei que estas conversões estavam tomando muito tempo de processamento e, para minha surpresa, a maior parte do tempo não era gasta com a conversão em si, mas com a instanciação da classe NSDateFormatter. Para solucionar o problema, bastou declarar o formatador como estático e instanciá-lo somente uma vez, criando uma espécie de cache. Esta modificação já surtiu um grande efeito, pois a atividade que estava sendo trabalhada envolvia, de uma só vez, o download, gravação e exibição de uma lista de entidades que possuíam 3 campos de data cada uma.

Lista dinâmica de propriedades

Dentre vários métodos do SPO, são feitas chamadas ao método propertiesWithEncodedTypes das entidades para obter um dicionário com todas suas propriedades e seus respectivos tipos. Esse método é relativamente custoso, pois esse dicionário é montado toda vez que o método é solicitado e, a cada busca por uma lista de entidades, podem ser feitas dezenas - ou centenas - de chamadas a este método.

Mais uma vez, a solução foi simples: cache. Depois da primeira chamada do método, o dicionário fica gravado numa variável estática que é consultada nas chamadas subsequentes. Resultado: mais um salto na performance.

Strings

As propriedades das entidades em Objective C são declaradas em camelCase, enquanto as respectivas colunas do banco seguem uma notação_com_underscores e para fazer essa conversão, o SPO define dois métodos em NSString-SQLiteColumnName.m. Como nos casos anteriores, esses métodos são muito solicitados durante as atividades, mas eu consegui fazer uma estatística que mostrava em em mais de 80% dos casos a conversão era desnecessária, pois a propriedade tinha a mesma notação tanto no código quanto na base. A mudança neste caso foi simplesmente verificar antes se o nome já estava no formato certo e retorná-lo imediatamente. Outro salto na performance.

Conclusão

Com as mudanças acima e utilizando a classe de benchmark, conseguir registrar melhoras de mais de 60% em diversas atividades do aplicativo. Registrei quedas, por exemplo, de 11 para 4 segundos, outras de 9 para 3.

Fiquei muito satisfeito com o resultado, principalmente por não ter que encarar uma mudança profunda no mecanismo de armazenamento do aplicativo tão próximo da entrada em produção.

Se você também utiliza o SQLite Persistent Objects na sua app, pode baixar o patch aqui.

Acessando a lista de contatos do iPhone dentro da sua app

Escrito por Karin em 16/09/09 18:56

Olá pessoal!

Depois que descobri que não mais poderia enviar SMS dentro da minha app, tive que mudar a funcionalidade que estava implementando para deixar de ser envio de SMS para ser de email. Mas eu não queria que este envio de email fosse feito fora da minha aplicação por meio do Mail App. Então decidi criar um formulário de email com a mesma facilidade de poder acessar a lista de contatos do iPhone e escoher o email de um contato já cadastrado.

Pesquisando, achei a referência no iPhone Dev Center de como acessar a lista. Mas como sou legal, segue um exemplo.

Primeiro, adicione um botão à sua view e crie um método para este botão no controller. Neste método, a tela de contatos será chamada e exibida:

- (IBAction)clickAdicionaEmail:(id)sender {
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init]; 
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES]; 
[picker release];   
}

A seguir, o delegate da classe ABPeoplePickerNavigationController deve ser implementado, conforme vemos abaixo:

- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker { 
[self dismissModalViewControllerAnimated:YES]; 
}

O método acima é chamado quando a ação de exibir a lista de contatos é cancelada. No nosso exemplo, simplesmente a tela é fechada.

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
return YES; 
}

Este segundo é chamado após um contato da lista é selecionado. Acima, este método retorna YES porque quero selecionar uma informação do contato e não o próprio, ou seja, se fosse necessário somente o nome do contato, era só pegar esta informação e retornar NO no método.

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
if (property == kABPersonEmailProperty) {
    CFTypeRef emails = ABRecordCopyValue(person, property);
    CFIndex indiceEmail = ABMultiValueGetIndexForIdentifier(emails, identifier);

    self.emailContato.text = (NSString *)ABMultiValueCopyValueAtIndex(emails, indiceEmail);
    [self dismissModalViewControllerAnimated:YES];
}

return NO;
}

Já o último é quando desejo selecionar um informação do contato, como, por exemplo, o email. No nosso caso, email é uma propriedade multivalorada, ou seja, o contato pode ter mais de um, por isso, precisamos saber exatamente qual queremos. No exemplo, qualquer email selecionado do contato é válido.

Pronto! É assim que conseguimos selecionar email dos nossos contatos do iPhone na nossa app.

Até a próxima :)

Leia também:

  1. Envio de SMS: Java ME x iPhone OS
  2. Base64 encode/decode para iPhone
  3. Saiba como customizar sua UITableView

Base64 encode/decode para iPhone

Escrito por Karin em 17/07/09 12:15

Bem, começo este post contando um pouco da história do uso de Base64 no nosso primeiro projeto. No Módulo Risk Manager, o cliente (iPhone) conversa com o servidor pelo protocolo HTTP. Informações como imagens e outros arquivos binários não podem ser transferidos diretamente por este protocolo, pois este exige que todos os dados sejam texto. Aí começa a nossa busca por um algoritmo de Base64 para iPhone.

O primeiro algoritmo que encontramos não foi bem sucedido, pois era muito lenta a conversão, o que prejudicava na performance do aplicativo. Após mais buscas pelo Google, encontramos uma lib chamada libtomcrypt. Nós gostamos muito dela, pois a performance melhorou em muito se comparado ao anterior. Porém, em uma certa situação, achamos um problema: se a string tivesse o tamanho igual a 10, a codificação era gerada erroneamente. Vasculhando a chamada do método do algoritmo, percebemos que o tamanho do buffer de saída que nós colocamos estava errado, então tomem cuidado com isso. Após o conserto, tudo voltou a funcionar sem problemas!

Legal, mas a história não acaba aqui.

No segundo projeto para a Módulo, Módulo Workflow Manager, nos deparamos com uma situação diferente da encontrada no projeto anterior, agora arquivos maiores seriam transitados entre o cliente e o servidor. E adivinha o que aconteceu? Isso mesmo, outro problema e agora era com gerência de memória. Novamente, fomos vasculhar a chamada do algoritmo para entender onde era o problema e conseguimos, bravamente, manipular a string de entrada para que fosse codificada em blocos, ou seja, dividimos a entrada em blocos de tamanhos pré-definidos para que manipulássemos melhor a memória do aparelho.

Ótimo, conseguimos trabalhar melhor com os arquivos, mas mesmo assim, limitamos baixar somente arquivos com tamanho 500Kb para não ter problemas de memória.

Mas não satisfeita, lendo um post do nosso amigo Matt Gallagher, vi que ele falava sobre Base64 e que disponibilizava o código. Fiquei feliz, mas precisava testar para ver se realmente funcionava. Entre um teste e outro, conseguimos validar o algoritmo, mas só uma dica, a saída do método de encode dele retorna com quebra de linha, então se você precisar que seja somente em uma linha, altere o parâmetro do método para false. Digo a vocês que o algoritmo dele funciona e o meu projeto está melhor do que com o outro. Os problemas, que o outro apresentava, não aconteceram com este. :)

Segue aqui o código-fonte do Base64 do Matt.

Ah, por todas essas buscas que fiz no Google, encontrei um site bem legal onde dada uma string, ele retorna o código em Base64. Segue o link.

Qualquer outra novidade, aviso! Valeu!

Leia também

  1. Mobits desenvolve software corporativo para o iPhone
  2. Mobits desenvolve mais um aplicativo para a Módulo
  3. Saiba como customizar sua UITableView
  4. Comparação entre as soluções ORM para iPhone
  5. Teste automático de interface para suas Apps de iPhone

Para aqueles que desejam obter o santo Graal desenvolvendo para a App Store, escrevemos, abaixo, os 10 mandamentos que aprendemos depois de dar muita cabeçada atrás dele:

10 Mandamentos para se tornar um desenvolvedor oficial para iPhone

1 - Não serás impaciente: Espere períodos indeterminados para aprovação da sua aplicação e dos seus contratos. O nosso contrato de desenvolvimento de apps gratuitas esperou de 20 de janeiro até 20 de maio na fila, é mole?

2 - Não colocarás acento, til ou c-cedilha no nome da sua empresa: Colocamos e olha como ficou nosso nome, só depois de muito ligar pra Apple é que conseguimos mudar o "õ" pra "o" e o "ç" pra "c".

C cedilha e Til não rolam

3 - Tentarás acessar o iTunes Connect assim que obtiveres a licença de desenvolvedor: Depois de obter a licença, desenvolvemos nosso aplicativo completo e quando fomos colocar na App Store, recebemos o erro: Apple ID does not have permission to access iTunes Connect (saiba mais). A solução: ligar pra Apple.

4 - Ligarás para a Apple usando o Skype: Depois de fazer muiiito DDI, descobrimos que é possível ligar para a Apple, no número 18006332152, gratuitamente usando o Skype. Basta ter uma boa conexão à Internet e esperar uns 10 minutos na linha até alguém te atender.

5 - Enviarás uma aplicação o mais rápido possível para a Apple: Eles colocam o seu contrato com prioridade baixa até você enviar uma aplicação e esta ser aprovada, ficando Pending contract (saiba mais). Confira esse trecho de um email recebido da Apple que confirma isso: "However, if you have an approved app waiting to go live, your contract setup will be prioritized to get your app in the App Store as quickly as possible":

App Pending Contract

6 - Não enviarás email para devcontracts@apple.com: Esse é o email "oficial" da Apple para tratar as questões contratuais - enviamos dezenas de emails para lá e nunca foram respondidos. O pior é que a Apple nos força a cair nessa pegadinha mandando respostas como "Please contact devcontracts@apple.com with this inquiry regarding your contracts." Ao invés deste email, siga o link ‘Contact Us’ no iTunes Connect. O ‘devprograms’ deverá lhe assistir no processo.

7 - Não enviarás fax para 408-974-9105: Desesperados após não receber resposta do devcontracts@apple.com, partimos para o fax. Esse é o número "oficial" do devContracts - nunca fomos respondidos.

8 - Enviarás o W8-BEN e Banking Info assim que conseguir a licença de desenvolvedor: Não deixe para submeter os dados do seu contrato pago para a última hora (quando o aplicativo estiver pronto) - ele demora meses para ser aprovado. Também achamos que existe uma relação entre o contrato gratuito (Free Applications Contract) e o pago, pois o nosso gratuito ficou meses pra ser aprovado e só foi quando submetemos o pago.

Contratos pendendo infinitamente

9 - Pagarás 30% de imposto para o governo norte-americano: As aplicações pagas que são vendidas para os EUA pagam, além de todos os impostos do Brasil e dos 30% da Apple, 30% para os EUA (pelo menos as Pessoas Jurídicas). É isso mesmo, não existe acordo para evitar a duplicidade no pagamento desse imposto entre o Brasil e os Estados Unidos. O pior é que grande parte dos brasileiros não compra pela App Store do Brasil, mas sim pela Americana, devido à questão de não termos a seção de jogos aqui.

10 - Para obter uma licença in-house, arrume um endereço nos EUA: Pois é... essa não teve jeito, quando precisamos de uma licença para desenvolvimento in-house (de US$ 299) para nosso primeiro projeto para iPhone, tentamos por mais de 5 meses, mas só conseguimos quando nosso cliente usou os dados da sede norte-americana. Para esse contrato, eles pedem dados que só as empresas de lá possuem.

Leia também:

  1. Licença da Apple
  2. Cine Mobits para iPhone na app store!

Formatador Automático para Objective C

Escrito por Felipe Barreto em 04/05/09 19:55

Uma das coisas que me incomodam na hora de programar pra iPhone é que o XCode não possui um mecanismo para formatar automaticamente o código como há no Eclipse ou no NetBeans. O máximo que a IDE da Apple permite é tabular automaticamente um trecho de código selecionado. Muito fraco.

Durante a pesquisa, foi difícil escolher o termo certo para encontrar o que eu queria, pois existem diversas variações: code beautifier, formatter, code styler, entre outras.

Finalmente, encontrei o tal do Uncrustify que é um programa de terminal para UNIX - logo, para MAC - e que permite configurar uma série de parâmetros de formatação e aplicar sobre um arquivo de código de diversas linguagens baseadas em C - inclusive Objective C.

"Estou no caminho certo", pensei eu. "Agora só falta aprender a instalar, configurar e fazer o XCode usar o programinha". Depois de penar um pouco, cheguei ao artigo que me colocou na direção que eu esperava. Nem tudo o que foi dito nele funcionou 100% ou está completo, mas foi de grande ajuda para conseguir alcançar o que vou apresentar abaixo.

Baixando e instalando o Uncrustify

  • Faça o download do uncrustify e extraia em qualquer lugar;
  • Abra o arquivo uncrustify.xcodeproj no XCode e dê um Build (Command-B);
  • Volte à pasta onde extraiu o Uncrustify e procure o executável no caminho build/ppc.

Agora, como meu conhecimento de UNIX é meio tosco, não garanto que o passo seguinte seja o mais correto, mas funcionou. :D

  • Copie o executável para a pasta /usr/bin (pode ser necessário se autenticar como admin).

Pronto! O Uncrustify está disponível para ser usado por qualquer usuário da máquina.

Criando o arquivo de configuração

Na pasta do Uncrustify, você vai encontrar o arquivo etc/defaults.cfg que possui praticamente todos os parâmetros disponíveis acompanhado de comentários bem explicativos. Mas eles são muitos e configurá-los um por um sem ver o efeito que causam sobre o código é impensável para alguém preguiçoso como eu.

Felizmente, alguém teve a bondade de criar um configurador para diversas ferramentas como o Uncrustify, inclusive o próprio: UniversalIndentGUI.

Esta ferramenta, apesar de ter a interface um pouco tosca e com usabilidade mediana, facilita muito o trabalho, pois permite editar os parâmetros e ver imediatamente o resultado sobre um trecho de código que você pode fornecer e manipular durante a fase de configuração.

Não vou me aprofundar sobre os detalhes desta fase, mas se você não estiver interessado em passar por ela, compartilho aqui o arquivo - ainda em desenvolvimento - com o padrão de código da Mobits.

Usando o arquivo acima, ou gerando o seu próprio, recomendo salvá-lo em /etc/uncrustify/qualquer_coisa.cfg para que esteja disponível para todos os usuários.

Colocando no XCode

Embora o XCode não possua diversas ferramentas comuns em outras IDEs, ele tem um mecanismo simples para adicionar funcionalidades sem muito esforço: os User Scripts.

Uncrustify oara ObjectiveC no XCode

  • Abra a tela de edição dos User Scripts e adicione um Shell Script com nome tipo "Auto Format";
  • Sugiro definir um atalho de teclado, como command-shift-A;
  • Copie o código abaixo para o seu script (renomeie o arquivo de configuração se necessário):
#!/bin/sh
echo -n "%%%{PBXSelection}%%%"
uncrustify -q -l OC -c /etc/uncrustify/mobits-uncrustify.cfg
echo -n "%%%{PBXSelection}%%%"
  • Configure os parâmetros de entrada e saída
    • Input: Entire Document
    • Directory: Selection
    • Output: Replace Document Contents
    • Errors: Display in Alert

Pronto. Agora basta executar o script sobre o código que estiver aberto no momento e apreciar sua beleza. :D

Até a próxima.

Leia também:

  1. Saiba como customizar sua UITableView
  2. Comparação entre as soluções ORM para iPhone
  3. Teste automático de interface para suas Apps de iPhone

Saiba como customizar sua UITableView

Escrito por Karin em 30/04/09 15:52

Oi pessoal,

estive lendo um artigo bem legal que diz como customizar a UITableView sem truques. Como está em inglês, resolvi resumir e escrever aqui para os desenvolvedores brasileiros.

Entendendo a UITableView

Abaixo, algumas coisinhas que devem estar claras sobre a UITableView:

  • Para configurar o background da UITableView com algo mais rebuscado que não seja simplesmente uma cor, você precisa "setar" o backgroundColor da tabela para [UIColor clearColor] e usar o background da view, que está por trás dela;

  • O tableHeaderView (e também tableFooterView e headers e footers das seções) não precisam ser necessariamente texto, ou seja, você pode inserir a sua própria view e ter a liberdade para criar o layout que quiser;

  • UITableViewCell é composta por cinco diferentes subviews. Para conseguir o layout que deseja, customizar as subviews de forma correta é o grande segredo:

  1. backgroundView — todo o background da linha;
  2. selectedBackgroundView — altera o backgroundView quando a linha está selecionada;
  3. image — imagem (que pode ser alterada) situada à esquerda da célula;
  4. accessoryViewview customizada situada à direita da célula;
  5. contentViewview customizada situada entre image e accessoryView.

Como tudo pode ser customizado, a criatividade pode ser aflorada, porém cuidado, pois quanto mais se utilizar imagens ou efeitos como gradiente maior o consumo de memória, o que pode ocasionar problemas de performance.

Exemplo

Neste artigo que li, existe um exemplo de como customizar uma tabela do zero. Segue abaixo:

Configurando a UITableView e o layout do header

- (void)viewDidLoad {
    //
    // Change the properties of the imageView and tableView
    // (these could be set in interface builder instead).
    //
    tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    tableView.rowHeight = 100;
    tableView.backgroundColor = [UIColor clearColor];
    imageView.image = [UIImage imageNamed:@"gradientBackground.png"];

    //
    // Create a header view. Wrap it in a container to allow us
    // to position it better.
    //
    UIView *containerView =
         [[[UIView alloc]
            initWithFrame:CGRectMake(0, 0, 300, 60)]
        autorelease];
    UILabel *headerLabel =
        [[[UILabel alloc]
            initWithFrame:CGRectMake(10, 20, 300, 40)]
        autorelease];
    headerLabel.text = NSLocalizedString(@"Header for the table", @"");
    headerLabel.textColor = [UIColor whiteColor];
    headerLabel.shadowColor = [UIColor blackColor];
    headerLabel.shadowOffset = CGSizeMake(0, 1);
    headerLabel.font = [UIFont boldSystemFontOfSize:22];
    headerLabel.backgroundColor = [UIColor clearColor];
    [containerView addSubview:headerLabel];
    self.tableView.tableHeaderView = containerView;
}

Background das células

UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger sectionRows = 
                   [aTableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
if (row == 0 && row == sectionRows - 1) {
    rowBackground = 
                  [UIImage imageNamed:@"topAndBottomRow.png"];
    selectionBackground = 
                  [UIImage imageNamed:@"topAndBottomRowSelected.png"];
}
else if (row == 0) {
    rowBackground = [UIImage imageNamed:@"topRow.png"];
    selectionBackground = 
                  [UIImage imageNamed:@"topRowSelected.png"];
}
else if (row == sectionRows - 1) {
    rowBackground = [UIImage imageNamed:@"bottomRow.png"];
    selectionBackground = 
                  [UIImage imageNamed:@"bottomRowSelected.png"];
}
else {
    rowBackground = [UIImage imageNamed:@"middleRow.png"];
    selectionBackground = 
                  [UIImage imageNamed:@"middleRowSelected.png"];
}
cell.backgroundView =
    [[[UIImageView alloc] initWithImage:rowBackground] autorelease];
cell.selectedBackgroundView =
    [[[UIImageView alloc] initWithImage:selectionBackground] autorelease];

Layout dentro do contentView

const CGFloat LABEL_HEIGHT = 20;
UIImage *image = [UIImage imageNamed:@"imageA.png"];

//
// Create the label for the top row of text
//
topLabel =
    [[[UILabel alloc]
        initWithFrame:
            CGRectMake(
                image.size.width + 2.0 * cell.indentationWidth,
                0.5 * (aTableView.rowHeight - 2 * LABEL_HEIGHT),
                aTableView.bounds.size.width -
                    image.size.width - 4.0 * cell.indentationWidth
                        - indicatorImage.size.width,
                LABEL_HEIGHT)]
    autorelease];
[cell.contentView addSubview:topLabel];

//
// Configure the properties for the text that are the same on every row
//
topLabel.tag = TOP_LABEL_TAG;
topLabel.backgroundColor = [UIColor clearColor];
topLabel.textColor = 
           [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
topLabel.highlightedTextColor = 
           [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];


Resultado e Considerações finais

Com o código acima, veja, abaixo, o resultado final. O código do exemplo está disponível no site do artigo.

UITableView

Ficou de boca aberta quando viu o resultado? Também fiquei, ainda mais porque a gente se sente meio preso com os layouts já existentes. Sabemos que as tabelas são os componentes mais usados em aplicações para iPhone e poder customizá-las do jeito que a gente quer (e o principal: não utilizando bacalhau) é muito bom.

Aproveitem para deixar as suas apps cada vez mais legais, mas não esquecendo de se preocupar com a performance!

Até :)

Comparação entre as soluções ORM para iPhone

Escrito por Felipe Barreto em 22/03/09 18:01

Começamos um novo projeto para nosso cliente e, por causa de alguns detalhes desta app, resolvemos experimentar o SQLite Persistent Objects - SLPO. Já falamos sobre o Active Record - AR - antes e das suas características como ORM para iPhone e agora estamos avaliando o SLPO.

Objetos não-persistidos

Até agora, a principal vantagem do SLPO é permitir a criação de objetos não-persistidos, ou seja, posso instanciar e manipular um objeto a vontade antes de salvá-lo no banco. Isso permite o uso de objetos temporários em telas onde o usuário pode popular uma entidade e, se desejar, cancelar todo o trabalho, sem que isso cause maiores dificuldades na implementação. O AR por sua vez nos obriga a criar value objects iguais a classe da entidade ou inserir os registros imediatamente na base e depois excluí-los se o usuário desistir da criação.

Por outro lado, o AR suporta habilitar ou não o delay writing, ou seja, posso decidir que toda vez que alterar um atributo, ele seja salvo automaticamente na base.

Criação automática da base

Enquanto o AR "deduz" os atributos das classes automaticamente conforme convenções no banco de dados, o SLPO faz o inverso: dados os atributos definidos na classe, o framework cria automaticamente a base de dados, as tabelas e os atributos. Essa abordagem é muito prática se você deseja começar a desenvolver rapidamente e seu modelo entidade-relacionamento é bem simples.

Contudo, como a maioria das ferramentas que automatizam um trabalho de criação, o resultado na base não é tão bem-feito como se fosse desenvolvido por um ser humano. O SLPO cria tabelas intermediárias onde somente uma foreign-key faria o trabalho e quando ele usa alguma foreign-key, esta normalmente é uma string, pois guarda a informação da tabela com que se relaciona.

Outro problema é que fico receoso de mexer na base por conta própria - para acrescentar uma trigger p. ex. - e prejudicar o funcionamento do framework. Já o AR funciona como no Ruby on Rails e utiliza convenções para deduzir nomes de classes, tabelas, atributos e relacionamentos. Conhecendo essas convenções simples, não é muito trabalhoso criar o banco para usar com a app.

Tipos dos atributos

Uma vantagem clara do SLPO é que ele suporta uma quantidade muito maior de tipos. Enquanto, o AR suporta basicamente NSString, NSData e NSNumber (sim, sem primitivas!), o SLPO suporta também UIImage, primitivas - int, float, double, etc - e qualquer classe que implemente o protocolo NSCoding, o que é ótimo, pois permite você persistir suas próprias classes como atributos no banco.

Resumo

Como todo blog que faz uma comparação entre tecnologia faz uma tabelinha, aqui vai a nossa:

Active Record SQLite Persistent Objects
Criação automática da base check
Definição automática das propriedades nas classes check
Objetos temporários check
Delay writing opcional check
Convenção sobre configuração check
Tipos primitivos check
Tipos personalizados check
Cache check check
Relacionamentos check check
Referência link link

Existem outras características de cada framework, mas essas foram as principais que serviram para escolhermos o que se adaptava melhor a cada projeto. Se você usou um dos dois, fique a vontade para comentar e contar sua experiência.

Abraço

Leia também:

  1. Teste automático de interface para suas Apps
  2. Mapeamento Objeto Relacional no iPhone

Novidades do iPhone OS 3.0

Escrito por Quintana em 19/03/09 10:18

Ontem foi lançada pela Apple, a nova versão do iPhone OS. A nossa previsão de aplicativos em background não se confirmou, mas foram muitas novidades lançadas. O Blog do iPhone fez um post explicando as principais. Para quem tem um iPhone e está pensando em atualizar o seu firmware para a versão 3.0 (quando esta for liberada) é uma boa leitura. Neste post, vou focar nas novidades do novo SDK para os desenvolvedores, como nós.

iPhone OS 3.0

Venda direta dentro da App

A primeira grande notícia para os desenvolvedores foi a nova possibilidade de poder vender conteúdo dentro do aplicativo, por exemplo, será possível fazer um jogo com algumas fases gratuitas e com outras fases que o jogador deverá pagar para jogar. A Apple continuará abocanhando 30% do valor vendido mas já é uma grande notícia poder trabalhar desta forma. Isto certamente irá diferenciar a App Store das novas lojas de aplicativo que entraram no mercado, como a loja da palm, comentada aqui, o Android Market e a Ovi Store.

Quem não roda em background, usa Push

Outra novidade importante é a notificação em plano de fundo. Já que não será possível rodar aplicações em background, a Apple resolveu substituir isto por uma API de Push, já conhecida de quem desenvolve em Java ME, onde os aplicativos poderão ser "acordados" quando um evento ocorrer. A justificativa da Apple para ter adotado esta solução e não a do background é que a segunda, segundo eles, consome mais bateria.

Bluetooth peer-to-peer, finalmente

Outra boa novidade é a comunicação peer-to-peer via bluetooth para jogos e aplicativos. Finalmente eles vão fazer algum uso do bluetooth, já que antes ele não servia pra muita coisa. Eu ainda acho pouco apenas a conexão P2P, pois com Java ME é possível conectar até 8 celulares em uma piconet bluetooth, mas P2P já é um começo.

1000 APIs

A Apple também afirma ter disponibilizado 1000 novas APIs para os desenvolvedores. Confesso que ainda não sei quais são - e acho que vou demorar um pouco pra descobrir, 1000 é muita coisa... -, mas já sei que agora será possível checar a rede de dados que está sendo utilizada (3G, EDGE, Wi-Fi), chamar a aplicação maps dentro da sua aplicação, ter acesso à biblioteca do iPod, utilizar o sensor de proximidade, transmissão de áudio e vídeo por HTTP, APIs embutidas de voz sobre IP (VoIP), entre outras.

O novo SDK do iPhone já está disponível para download pelos desenvolvedores cadastrados no programa de desenvolvimento da Apple. O download tem 2.1 Gigas e eu já estou fazendo o meu (isso significa que a Mobits finalmente conseguiu a licença da Apple).

Em breve mandaremos nossas impressões sobre o novo SDK.

Leia também:

  1. Análise de plataformas móveis: iPhone (Prós e contras)
  2. Licença da Apple

Teste automático de interface para suas Apps

Escrito por Felipe Barreto em 03/03/09 00:24

Durante o desenvolvimento do nosso primeiro projeto para iPhone, encontramos o artigo do Matt Gallagher onde ele apresenta uma técnica para simular toques numa aplicação rodando no simulador do iPhone e, consequentemente, realizar testes automáticos de interface.

Eu já havia trabalhado com testes de interface para sistemas web com o Selenium e sempre gostei do poder que esta ferramenta acrescenta aos nossos teste e, portanto, fiquei muito feliz em ver esta possibilidade para o nosso projeto e coloquei logo em prática a técnica do Matt.

Observei algumas limitações bem como algumas dificuldades e comecei a modificar o código do Matt, chegando a soluções mais parecidas com o Selenium e achei que o troço tava ficando bom e merecia virar um projeto Open Source. Pedi então a autorização e o apoio do Matt para criar um projeto e tocar as ideias para a frente.

Eis que surge o Bromine!

Bromine Interface Test/Testes de interface Bromine - Bromo em Inglês, elemento seguinte ao Selênio na Tabela Periódica - está disponível para download via SVN no Google Code pelo endereço http://code.google.com/p/bromine/.

O Bromine possui uma seção wiki explicando como baixar e instalar o framework na sua aplicação, mas se tiver alguma dúvida não hesite em entrar em contato. E se quiser contribuir, melhor ainda! :D

Como funciona?

A ideia é simples: o Bromine disponibiliza comandos que permitem o desenvolvedor acessar e manipular os elementos da tela, deste modo, você pode, por exemplo, verificar se a tela que você está possui um título X, preencher uma caixa de texto e clicar num botão. Tudo isso automática, rápida e repetidamente, como um bom teste deve ser.

Para fazer essa mágica, o Bromine representa sua interface como um grande XML e seus comandos permitem acessar as views através de simples sentenças XPath.

Em breve, postarei um vídeo do Bromine em funcionamento para ilustrar melhor. Aguardem!

Testes no iPhone

Escrito por Felipe Barreto em 09/01/09 20:54

Este é um assunto que aprendi a dar valor no meu último projeto antes da Mobits e que consegui trazer para o nosso projeto atual por causa da sua importância para o desenvolvimento de um software de qualidade: os Testes Unitários.

Não vou me alongar sobre os benefícios do TDD (Test Driven Development), pois acho que há muita referência na internet e quem já experimentou sabe de como esse método contribui para um código limpo, bem organizado e com maior resistência a falhas.

Vamos direto ao assunto, então.

Ruby to the rescue

Logo na minha primeira busca, fiquei muito feliz de descobrir que um dos mestres do mundo digital o Dr. Nic já havia tido a bondade de contribuir com um projeto que permite escrever testes em Ruby para suas classes ObjectiveC. Não perdi tempo e comecei a fuçar a novidade.

Utilizei o framework por algum tempo, contudo comecei a querer fazer alguns testes mais complexos e incluir alguns frameworks novos - p. ex. ActiveRecord para iPhone que já comentamos. E, por causa dessa necessidade, os testes passaram a não funcionar como eu gostaria. Gastei um bom tempo tentando editar o próprio framework de teste para me atender, mas foi em vão.

Resumindo, os testes em Ruby são excelentes para o aprendizado, para realizar testes simples sobre classes NSObject, mas começam a complicar a medida que você testa coisas mais complexas.

Ok, voltando ao Objective C

Desmotivado, sem vontade de cantar uma bela canção, resolvi buscar alternativas dentro do mundo ObjectiveC e encontrei o framework de testes do GTM - Google Toolbox for Mac. Com esse framework consegui testar tudo que precisava e pude continuar a tocar o projeto com os queridos testes.

Para usar o GTM no seu projeto faça o seguinte:

  1. Baixe o GTM e extraia para uma pasta qualquer ;
  2. No seu projeto no XCode, crie um novo iPhone Target (Cocoa Touch Application) via Project > New Target... e nomeie como algo como Testes Unitarios ;
  3. Modifique o Active Target para o novo target;
    selecionando o target
  4. Crie um novo grupo GTM na raiz do seu projeto;
  5. Adicione os seguintes arquivos ao grupo (garantindo que estejam associados somente ao target de testes):
    • google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestMain.m
    • google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestDelegate.m
    • google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestDelegate.h
    • google-toolbox-for-mac/UnitTesting/GTMSenTestCase.m
    • google-toolbox-for-mac/UnitTesting/GTMSenTestCase.h
    • google-toolbox-for-mac/GTMDefines.h

    adicionando gtm ao target
  6. adicione uma build phase ao seu target do tipo run script atraves do menu Project > New Build Phase > New Run Script Build Phase. Arraste para o fim das atividades do seu target se for necessário;
    criando build phase
  7. Duplo-clique nessa nova build phase para editar. Defina o shell como "/bin/sh" e o script como "CAMINHO_GTM_NA_SUA_MAQUINA/UnitTesting/RunIPhoneUnitTest.sh";
    editando build phase
  8. Execute o Build - não Build and Go. Abra o Build Results (command+shift+B) para ver o resultado dos testes.
    build com sucesso

Escrevendo os testes

Para organizar, crie uma pasta para seus testes (ex. Testes :P) e adicione ao projeto. Cada classe do projeto que você quiser testar, basta adicionar seu arquivo .m* ao target de teste (ele passará a estar associado aos dois targets) e criar uma classe de teste correspondente, convencionada como *NomeDaClasseTestadaTest.m - embora este formato não seja obrigatório.

classe do projeto no target de teste

Uma sugestão para facilitar a manipulação dos testes, é manter a @interface e @implementation no mesmo arquivo .m, ou seja, dispensando o arquivo .h das classes de teste.

classe de teste

Todos os métodos que serão executados como teste, devem iniciar por test e não precisam ser declarados na interface. O método setUp pode ser sobrescrito e será executado sempre antes de cada método de teste. O mesmo ocorrerá com o método tearDown no final de cada teste.

Quando escrever o teste, você poderá utilizar os asserts que o GTM lhe provê: todos estão definidos começando com STAssert... e se encontram no GTMSenTestCase.h. A qualquer momento, rode o Build e verifique os erros. Note que cada erro sempre aparece duplicado.

build failed

Considerações

Se estiver usando controle de versão, talvez seja interessante que os arquivos do GTM estejam dentro da pasta do seu projeto. Não há a necessidade de copiar a pasta inteira, somente os arquivos que citei acima, preservando a estrutura de diretórios.

Existem outros recursos que precisei desenvolver ou obter para atender minhas necessidades como uma imitação de test fixtures para controlar meu banco de testes e uma biblioteca de mock para abstrair algumas dependências a classes mais complexas. Pretendo abordar o uso destes em futuros posts.

Se o simulador do iPhone estiver aberto durante os testes, dá um erro esquisitão. Verifique sempre que ele esteja fechado.

Bom Desenvolvimento Orientado a Testes para todos e até a próxima!

Mapeamento Objeto Relacional no iPhone

Escrito por Quintana em 06/01/09 14:02

Mapeamento Objeto Relacional (ORM) é a técnica de desenvolvimento utilizada para representar as tabelas de um banco de dados relacional em classes e objetos em linguagens orientadas a objetos. Os objetos são persistidos no banco relacional sem a necessidade da utilização de SQL, que é utilizada apenas para consultas mais complexas.

A plataforma de desenvolvimento do iPhone não possui nenhuma solução nativa para ORM, contudo, existem alguns frameworks desenvolvidos por terceiros e disponíveis na internet que podem conectar os objetos na linguagem Objective-c com o banco de dados SQLite do iPhone. No nosso projeto estamos utilizando o Active Record.

O Active Record é um padrão muito utilizado no Ruby on rails. Para utilizá-lo no iPhone, é preciso baixar o código no site do projeto e incluí-lo na sua aplicação. Depois siga o passo a passo a seguir:

  • Crie um banco de dados com o sqlite3, como o abaixo:


  CREATE TABLE alunos
     (
     id  integer primary key,
     nome varchar(255),
     inscricao varchar(255)
     );

  • Arraste o banco para o seu projeto e o coloque no grupo Resources

  • Desenvolva uma classe no seu projeto, como a seguinte:



    @interface Aluno : ARBase {}

    @property (readwrite,assign) NSString *nome, *inscricao;

    @end

    @implementation Aluno

    @dynamic nome,inscricao;

    @end

Todas as classes que serão mapeadas devem herdar de ARBase. Como podemos perceber, o nome da tabela é sempre o nome da classe em minúsculo e no plural. Esse padrão é utilizado pelo Active Record para relacionar a tabela com a classe. Se o nome de sua tabela estiver em português e o plural for diferente do gerado pelo Active Record, sobrescreva o método de classe tableName na classe a ser mapeada e retorne o nome da tabela.

  • Escreva o código para manipular as classes, como o seguinte:


for (Aluno *aluno in [Aluno findAll]) {

  NSLog(@"Nome:%@; Inscrição: %@",aluno.nome,aluno.inscricao);

}

As classes sempre são persistidas no banco. Quando você cria uma nova instância de aluno, o Active Record criará um registro na tabela aluno automaticamente. Isso às vezes é ruim, pois não é possível utilizar objetos não persistidos, como na API JPA do Java. Utilize VOs para fazer isso.

Um dos principais problemas que notamos no Active Record foi a sua lentidão. Quando testamos o nosso sistema no SDK, estava tudo ótimo, mas quando testamos no iPod Touch, percebemos que o programa demorava bastante para exibir listas de objetos persistidos e seus atributos.

Fuçando o código-fonte, percebemos que o mecanismo do Active Record funciona da seguinte maneira: primeiro ele obtém os ids de todos os objetos que queremos exibir, depois a cada vez que queremos buscar um atributo no banco, ele faz uma consulta SQL e obtém o atributo individual. Para exibir o resultado do código acima, se tivermos 10 alunos no banco, ele fará 21 consultas SQL, 1 para obter os IDs, 10 para obter o nome dos alunos e 10 para obter as inscrições. Na verdade, só precisamos de uma consulta e é o que eu sugiro que deva ser feito para casos muito grandes: utilizem consultas SQL ao invés da busca do Active Record.

Uma coisa que ajuda no desempenho é o uso do cache, que mantém o resultado das consultas em memória para evitar outras consultas ao banco.

Mas apesar das desvantagens, o Active Record ainda traz muitos ganhos ao mapear objetos, relacionamentos e automatizar toda a parte de conexões com o banco. Contudo, ele ainda tem que evoluir muito. Outra opção para o Active Record é o sqlitepersistentobjects. Eu ainda não testei, mas pretendo fazê-lo em breve, pois tenho lido boas impressões sobre ele. Quando eu testar, postarei no blog.

Até +