Resultados para ''

Enviando SMS de dentro de um aplicativo iPhone

Escrito por Quintana em 15/08/10 16:20

O envio do chamado In-App SMS é uma das novidades do iOS4. Ela pode ser muito útil em aplicativos como o Cine Mobits, onde um usuário poderá enviar convites via SMS a seus amigos para assistir um determinado filme. Contudo, como podemos implementar uma funcionalidade útil como essa do iOS 4 sem perder a compatibilidade com os sistemas operacionais anteriores e nem com os dispositivos, como o iPod Touch que não tem SMS?

Simples! O primeiro passo é baixar o Xcode do iOS 4. Com ele você vai reparar que não é mais possível compilar aplicações para o iOS 3, apenas para o 4.0 ou para o 3.2 (iPad). Para que dispositivos com sistemas operacionais anteriores possam rodar um aplicativo compilado em 4.0 é preciso primeiro alterar o Deployment target do seu projeto para a mínima versão que você quer que o app execute.

Xcode

Ao fazer isso, a Apple recomenda que você verifique se as funções que irá utilizar (especialmente as novas APIs) estão presentes no dispositivo. Para fazer isso no nosso exemplo do SMS, é só fazer o seguinte código:

Class messageClass = (NSClassFromString(@"MFMessageComposeViewController"));

// Verifica se a classe existe no sistema operacional
if (messageClass != nil) {      

    // Checa se o dispositivo pode enviar SMS
    if ([messageClass canSendText]) {
        [self displaySMSComposerSheet];
    }
    else {  
        feedbackMsg.hidden = NO;
        feedbackMsg.text = @"Esse dispositivo não pode enviar SMS.";

    }
}
else {
    feedbackMsg.hidden = NO;
    feedbackMsg.text = @"Esse dispositivo não pode enviar SMS.";
}

Reparem que para evitar um erro de execução em sistemas operacionais a classe MFMailComposeViewController não foi chamada diretamente. Depois disso, também precisamos verificar o caso do dispositivo possuir ou não suporte a SMS.

Para enfim enviar o SMS, temos que usar o MFMessageComposeViewController, que é a tela onde escrevemos o SMS que nem no app original do iPhone, ou seja, a Apple não permite que o aplicativo envie um SMS sem a confirmação do usuário. A única vantagem é que não precisamos mais sair do app e podemos colocar uma mensagem e vários números de telefone na tela:

-(void)displaySMSComposerSheet 
{
    MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
    picker.messageComposeDelegate = self;

    picker.body = @"Teste de SMS do iPhone";
    picker.recipients = [NSArray arrayWithObjects:@"12345678", @"87654321", nil];
    picker.messageComposeDelegate = self;

    [self presentModalViewController:picker animated:YES];
    [picker release];
}

Essa é a imagem da tela de envio de SMS:

In App SMS

Em breve vamos implementar essa funcionalidade no Cine Mobits, bem como nas versões Java e Android onde ela já existe. Até a próxima!

Leia também:

Usando XML em seus projetos iPhone

Escrito por Karin em 28/07/10 20:43

Oi pessoal. Aqui na Mobits a gente trabalha bastante com parser de dados. As duas formas mais comuns são: XML e JSON. Hoje eu vou falar um pouco como usar XML em projetos iPhone. Em um post futuro, será sobre JSON.

Primeiros passos

Nos nossos projetos, utilizamos partes da Google Data APIs Objective-C Client Library que fornece diversas ferramentas de desenvolvimento para Mac e iPhone.

Para começar a trabalhar com XML em seu projeto, baixe o projeto completo do Google ou, para simplificar, somente o arquivo XML.zip onde selecionamos apenas os recursos necessários para este exemplo. Depois, siga os seguintes passos:

  1. Crie uma pasta XML na raiz do seu projeto e copie GDataDefines.h e a pasta XMLSupport para a pasta;

  2. No Xcode, adicione a pasta criada ao seu projeto;

  3. Adicione também libxml2.dylib à lista de Frameworks;

  4. Em Get Info do seu projeto, na aba Build, para todas as configurações, adicione em Header Search Paths:
    /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/usr/include/libxml2 (Observe a versão do iOS no caminho acima. Pode ser necessário mudar de acordo com a versão utilizada pelo projeto.);

  5. Pronto. Agora você poderá usar o framework para fazer parser de XML.

Implementação

Agora que já temos o framework configurado em seu projeto, vamos usá-lo para fazer parser de um XML. Abaixo, um exemplo de XML:

<?xml version="1.0" encoding="utf-8"?>
<projetos>
    <projeto>
        <nome>Cine Mobits</nome>
        <descricao>Veja os horários do cinema direto do seu celular! Disponível para a maioria dos celulares com suporte a Java, iPhone/iPod Touch e também celulares com Android.</descricao>
    </projeto>
    <projeto>
        <nome>Mobits Button Soccer</nome>
        <descricao>O clássico futebol de botão, jogo presente na infância de muitas pessoas, agora em versão para iPhone/iPod Touch.</descricao>
    </projeto>
    <projeto>
        <nome>FaxGuru</nome>
        <descricao>Envie fax direto do seu iPhone! O FaxGuru permite que você tire fotos de documentos e envie-os via fax para mais de 250 países, além de ser simples, fácil de usar e totalmente em português.</descricao>
    </projeto>
</projetos>

A seguir, o código de parse:

#import "GDataDefines.h"
#import "Projeto.h"

@implementation ProjetosParser

+ (NSArray *)parse:(NSString *)xml {
    NSError *error = nil;

    NSXMLDocument *document = [[NSXMLDocument alloc] initWithXMLString:xml options:0 error:&error];
    NSXMLElement rootElement = [document rootElement];

    NSArray *projetosDoXML = [rootElement nodesForXPath:@"//projeto" error:&error];

    NSMutableArray *projetos = [NSMutableArray arrayWithCapacity:[projetosDoXML count]];

    for (NSXMLElement *projetoDoXML in projetosDoXML) {
        Projeto *projeto = [[Projeto alloc] init];
        projeto.nome = [[[projetoDoXML nodesForXPath:@"./nome/text()" error:nil] lastObject] stringValue];
        projeto.descricao = [[[projetoDoXML nodesForXPath:@"./descricao/text()" error:nil] lastObject] stringValue];

        [projetos addObject:projeto];
        [projeto release];
    }

    [document release];

    return projetos;
}

@end

A primeira coisa a ser feita é importar "GDataDefines.h". Se preferir, pode fazer a importação no pch e o parser ficará acessível para todas as classes do projeto.

Para começar a fazer o parse a partir de uma string XML, é preciso transformá-la em NSXMLDocument. Assim, conseguimos obter o elemento raiz do nosso XML. A partir daí, é só fazer as consultas XPath para obter as informações desejadas. No nosso exemplo, o método retorna um array de projetos.

Dica

Caso você se depare com um XML onde a raiz tem namespaces, a consulta XPath será da seguinte forma:

<projetos xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">

projeto.nome = [[[projetoDoXML nodesForXPath:@"./*[local-name()=nome]/text()" error:nil] lastObject] stringValue];

Espero que este post seja útil! Como disse, o meu próximo assunto será sobre JSON. Aguardem!

Removendo acentuação em Objective C

Escrito por Felipe Barreto em 03/07/10 07:00

Para os programadores brasileiros, a acentuação das strings é um fator importante a ser considerado em vários aspectos do software, como buscas, comparações, ordenações e visualizações. Por isso, em alguns casos pode ser interessante transformar os caracteres acentuados em não acentuados como p. ex. "Meu app é móvel" viraria "Meu app e movel".

Se você procurar na Internet, vai encontrar as mais diversas abordagens para realizar essa tarefa, desde a pura força bruta (fazendo um find-and-replace por cada caractere acentuado conhecido e substituindo pelo seu equivalente) até abordagens um pouco mais inteligentes. Contudo, poucas são tão simples como a que veremos a seguir.

Para fazer essa transformação, a primeira coisa é entender como o charset que você está usando funciona. Como na grande maioria dos casos usamos UTF-8, me basearei neste padrão para o código que desenvolvi.

Como explica muito bem este artigo, o UTF-8 representa cada caractere com 1 a 4 bytes, enquanto outros padrões como o ASCII e o ISO-8859-1 utilizam apenas 1 byte. O interessante do UTF-8 é que os caracteres acentuados não são nada mais que a união do caractere básico e seu acento (ambos ASCII), como, p. ex., "é" é composto pelos bytes dos caracteres "e" e "'". Somente com este conceito, já fica fácil imaginar qual deve ser a solução.

Basta separarmos os bytes da string, ignorar os bytes dos acentos e juntarmos novamente. E uma forma muito simples de fazer isso é converter sua string para ASCII e depois convertê-la de volta a UTF-8.

Assim:

NSString *minhaString = @"É fácil remover acentos. Até de pingo d'água :D";
NSData *minhaStringAsciiData = [minhaString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *minhaStringSemAcentos =  [[[NSString alloc] initWithData:minhaStringAsciiData encoding:NSUTF8StringEncoding] autorelease];
// minhaStringSemAcentos => "E facil remover acentos. Ate de pingo d'agua :D"

Três observações sobre o código acima. Na segunda linha está o truque principal deste código: allowLossyConversion:YES. Esse parâmetro define que a conversão de UTF-8 para ASCII permitirá que haja uma perda de informação na conversão. Esta perda é justamente a dos bytes que armazenam os acentos, simples não?

A segunda observação é que caracteres que normalmente são usados como acentos, mas em situações em que não são - como o ' em d'agua - não são perdidos pois nestas situações, eles ocupam o byte principal na string UTF-8 e não há perda na conversão.

Finalmente, é importante observar que o código acima funciona apenas para strings comuns na língua portuguesa. Para outros caracteres especiais, pode haver casos de inconsistências. P. ex. caracteres gregos como α, β, etc. são convertidos para caracteres totalmente diferentes ou simplesmente inválidos. Se o seu software espera strings com caracteres mais variados, talvez este código não se aplique e você deva buscar outras soluções.

Leia também:

Fragmentação no iPhone?

Escrito por Afonso Junior em 29/06/10 17:00

Já é lugar-comum dizer que o perfil dos usuários de celular mudou muito. Agora, além de um bom aparelho, os aplicativos são determinantes para se escolher um novo celular: quanto mais aplicativos existirem, mais atrativo fica o aparelho. Por conta disso, tudo que for feito no sentido de melhorar a vida do desenvolvedor acaba sendo bom também pro fabricante do dispositivo, pois mais desenvolvedores implica mais aplicativos, enfim, tudo o que o usuário final deseja.

Porém, há um fator que sempre atrapalhou os desenvolvedores na hora de criar o seu aplicativo: a fragmentação. Pra quem não sabe o que isso quer dizer, vou explicar através de um exemplo. Quando desenvolvemos para Java ME, nunca temos certeza se o aplicativo irá funcionar em todos os aparelhos. Isso se deve a diversos fatores, seja porque a fabricante decide instalar uma versão mais antiga (ou mais nova) do CLDC, ou por questões de compatibilidade do hardware que se usa no dispositivo, enfim, sobra para o desenvolvedor testar em diversos aparelhos, e esperar que funcione na maioria deles.

Esse problema não ocorre só com Java ME, também acontecia com Windows Mobile: muitas vezes soluções corporativas eram desenvolvidas e homologadas para um aparelho, e as empresas ficavam presas àquele modelo, pois não havia garantias de que iria funcionar com outro, mesmo sendo da mesma fabricante.

Daí veio o desenvolvimento para iPhone, e esse problema quase não existia: praticamente sempre o mesmo hardware, poucas alterações entre as atualizações do sistema operacional do aparelho, mesmo tamanho de tela, enfim, a vida para nós desenvolvedores foi facilitada. Hoje em dia, um dos principais concorrentes do iPhoneOS, o Android, embora tenha um bom SDK, sofre muito com esse problema. Basta colocar no Google "fragmentação android" e ver que há diversos textos tratando disso como não só como um problema, mas como uma ameaça à plataforma.

Porém, nos últimos meses surgiram diversas novidades vindas da empresa de Cupertino: surgiu o iPad, com tamanho diferente de tela e sem câmera, veio o novo sistema operacional - o iOS4 -, e com ele a notícia de que os aparelhos mais antigos (2G para baixo) não poderiam ser atualizados, e o novo iPhone, também com nova resolução de tela. Por conta disso, começou a rolar na internet alguns textos que afirmavam que estaria começando o processo de fragmentação do iPhone.

Não se pode negar que alguns percalços realmente existam por causa dessa avalanche de atualizações: aqui na Mobits, por exemplo, só temos um computador habilitado para fazer build abaixo da versão 3.2. Isso sem contar que, para fazer um aplicativo universal, a própria Apple afirma que você deverá ter mais trabalho. Porém ainda não é nada perto ao que encontramos nas outras plataformas. Não se trata de uma defesa cega da Apple (como se ela precisasse), mas desenvolver para iPhone ainda é mais fácil do que para as outras plataformas. E isso acaba explicando o porquê ela tem tantos aplicativos disponíveis, enquanto outras, como Symbian, vão minguando no mercado de smartphones.

Leia também:

Exibindo conteúdo tridimensional modelado no iPhone

Escrito por Quintana em 04/06/10 19:43

Quem já começou a programar com o OpenGL no iPhone já deve ter executado o programa de exemplo da Apple (GLGravity) que exibe uma chaleira bonitinha no iPhone que gira de acordo com o acelerômetro:

teapot

A grande questão é que essa chaleira não é carregada de um arquivo que foi modelado em alguma ferramenta, como o Maya ou o Blender. Ela é um array de vértices e de vetores normais escritos em um arquivo.h e importados pelo Objective-c, onde esse array é iterado e cada vértice desenhado é na mão no código do OpenGL:

arquivo .h

Um ponto importante é que o iPhone não usa o OpenGL normal, por ser um dispositivo móvel com processamento limitado, ele usa uma versão mais simplificada do mesmo, chamada de OpenGL ES. No OpenGL ES todos os objetos tridimensionais são desenhados como um conjunto de triângulos e estes triângulos por sua vez são formados por 3 vértices (ou pontos) e possuem apenas um lado visível (para diminuir o processamento), sendo que o lado que não fica visível geralmente é o de dentro dos objetos.

Fato que desenhar objetos complexos com triângulos via código seria uma missão muito complicada, e esse é o objetivo desse post: demonstrar como podemos transformar objetos modelados em ferramentas profissionais em um arquivo .h com os vértices dos triângulos que compõem um objeto e que sejam compatíveis com o OpenGL ES.

Primeira tentativa: obj2opengl

A minha primeira tentativa em fazer essa conversão foi usando o script em Perl descrito nesse link. A proposta é ótima! Usando uma ferramenta de modelagem, nós exportamos o objeto desejado para o formato Wavefront (.obj), tomando sempre o cuidado de transformar os objetos que serão exportados para triângulos antes (as próprias ferramentas tem suporte pra isso). Depois, basta rodar o script, que pode ser baixado nesse link, passando o arquivo .obj como parâmetro que ele gera o arquivo.h para você:

./obj2opengl.pl objetoModelado.obj

Os primeiros testes (com cubos modelados) foram um sucesso. Foi quando eu achei que havia achado a solução do problema, então comecei a modelar objetos mais complexos. Foi aí os erros começaram a aparecer. Notei que essa solução não estava funcionando bem em modelagens mais complexas com vários objetos, texturas e tudo mais. Algumas vezes ela dava um erro de divisão por zero e em outras apresentava erros na exibição do objeto final.

A solução: Script do Blender

Então procurando pela internet, achei mais uma solução, do Jeff Lamarche, aliás eu já conhecia o Jeff por causa de uma série de posts ótima que eu havia lido dele, explicando passo a passo o OpenGL ES no iPhone. A solução que ele desenvolveu foi um script em Python que funciona direto do Blender (como se fosse um plugin) e permite que você exporte seus objetos diretamente para os arquivos headers que são recomendados pela Apple no OpenGL ES do iPhone.

Dessa vez os testes foram ótimos, todos os objetos modelados foram exportados com sucesso, inclusive o Jeff lançou uma atualização para o seu script que já converte os objetos para triângulos diretamente e também captura as informações de textura dos mesmos, que na versão anterior tinham que ser inseridas no código depois da importação.

Para ilustrar, criei um exemplo com o conhecido macaco do Blender rodando no openGL do iPhone:

macaco do blender no iphone

Quem tiver uma solução alternativa, sinta-se livre para comentar! :)

Leia também:

iPad: primeiro projeto com UISplitView

Escrito por Afonso Junior em 17/05/10 18:30

O iPad roda o mesmo sistema operacional utilizado pelo iPhone e iPod Touch. Isso significa que os desenvolvedores que já estão familiarizados com programação em iPhone são capazes de escrever rapidamente aplicativos para o iPad. Na verdade, a maioria (se não todas) as aplicações do iPhone deve ser executada sem nenhum problema no iPad, embora em um tamanho menor de quadro, com uma opção de aumentar o tamanho dos pixels. No entanto, para realmente fazer pleno uso da tela maior do iPad, você precisa redesenhar sua interface de usuário para ter em conta o espaço extra de tela. O iPhone SDK 3.2, que é necessário para o desenvolvimento para iPad, vem com um novo modelo de aplicação, Split View-based Application, que permite construir aplicações em wide-screen.

O modelo Split View-based permite que você crie uma interface de visualização dividida em sua aplicação. O lado esquerdo da tela exibe uma lista de itens selecionáveis, e o lado direito exibe os detalhes do item selecionado. Este artigo mostrará como desenvolver um aplicativo baseado nesse modelo para o iPad.

Começando o projeto

Na janela de diálogo de novo projeto, escolha o "Split View-based Application". Dê um nome para o seu projeto (o meu será um projeto de Portfolio).

Split View-based Application

Observe na imagem abaixo que além do AppDelegate ele cria mais duas classes: o RootViewController e o DetailViewController. Mais tarde eu explicarei como eles se encaixarão nessa dinâmica toda.

Classes geradas

Mande rodar o projeto (Command + R) e veja como ficam as telas nas figuras abaixo. Quando o iPad está deitado, a lista fica visível à esquerda. Quando em pé, ele aparece como um menu suspenso.

Split View no iPad: paisagem

Split View no iPad: retrato

Olhando o AppDelegate

Se olharmos o código de PortfolioAppDelegate.h,

Código no AppDelegate

podemos ver que ele tem um UISplitViewController. Esse elemento vai "abrigar" dois viewControllers: o RootViewController e o DetailViewController - que, respectivamente, serão a lista com os itens a serem selecionados e a tela que exibe algo de acordo com a seleção da lista.

Na implementação do PortfolioAppDelegate, nenhuma novidade:

Código no AppDelegate (implementação)

Somente adiciona a view do splitViewController na window.

Olhando o XIB

MainWindow no Interface Builder

Abrindo o arquivo MainWindow.xib, podemos ver que ele tem um Split View Controller. Dentro dele, dois elementos: o NavigationController e um view controller (cuja classe é referenciada pela classe DetailViewController no projeto). Dentro do NavigationController,

RootViewController no Interface Builder

temos um Navigation Bar e outro viewController, referenciado pelo RootViewController do projeto.

RootViewController no Interface Builder

No AppDelegate, algo que já era de se esperar:

RootViewController no Interface Builder

todos os elementos com propriedade IBOutlet no PortfolioAppDelegate.h ligados com seus correspondentes no XIB.

Olhando os ViewControllers

Depois de olharmos o XIB, vamos ver como a mágica acontece dentro dos dois viewControllers do projeto. Numa rápida olhada no RootViewController.h

Código do RootViewController

percebemos que ele é filho de um UITableViewController, que servirá para tabelar os valores a serem exibidos na tela. Eu criei o array portfolio que guardará os valores que serão exibidos na tela.

Agora, olhando o código de DetailViewController.h,

Código do DetailViewController

podemos perceber que ele implementa dois protocolos:

  • UIPopoverControllerDelegate: para controlar o popover que irá exibir o conteúdo da tableView do RootViewController quando o iPad estiver em pé (posição retrato);
  • UISplitViewControllerDelegate: para dizer o que fazer (esconder ou exibir) o PopoverView quando o iPad mudar a orientação.

Na implementação, o Xcode gera para o DetailViewController quatro métodos que nos interessam para o nosso exemplo. O primeiro,

Código do DetailViewController

serve para utilização do detailItem dentro da classe. Na declaração da classe ele foi declarado como um id, e a ideia é que ele seja utilizado para popular os valores a serem exibidos na tela. No RootViewController, eu atribuí a ele um NSDictionary com duas chaves: o texto a ser exibido e o nome da imagem a ser mostrada. Mas a ideia é não mexer muito nesse método, e sim no próximo:

Código do DetailViewController

Olhando o código você pode perceber que as propriedades da label e da imagem são alteradas, ou seja, é aqui que parte da "mágica" acontece. Os outros dois métodos são implementações dos protocolos assinados:

Código do DetailViewController

Confesso que mexi quase nada neles. O primeiro é acionado quando a tela é rotacionada para o modo retrato (em pé). E o outro, o contrário: ele é o que faz o RootViewController ser exibido novamente quando o aparelho é posto em modo paisagem.

Enfim, com um código simples já consigo fazer uma aplicação de portfólio. Para facilitar, eu coloquei em dois arquivos .plist a relação entre os nomes das posições na tableView com os arquivos de imagem e com suas descrições. E criei uma classe utilitária para fazer o acesso aos arquivos. Tudo isso você pode consultar baixando o projeto. Abaixo segue algumas telas do projeto.

Exemplos do projeto

Exemplos do projeto

Exemplos do projeto

Exemplos do projeto

Leia também:

Com o lançamento do iPhone OS 4, muito tem se falado sobre as suas novas funcionalidades como a multitarefa ou a organização dos aplicativos em pastas. Mas não podemos nos esquecer que, desta vez, bem como no iPhone OS 3.0, a Apple lançou mais de 1000 novas APIs que trarão grandes oportunidades aos desenvolvedores e certamente vão revolucionar mais uma vez a plataforma com novos aplicativos que agora são viáveis. Vamos comentar as principais.

APIs iPhone OS 4

SMS dentro dos Apps

Já havíamos criado um post no passado falando exatamente sobre essa limitação que nos impedia de mandar SMSs no Cine Mobits. Agora com essa nova API, certamente veremos uma nova gama de apps com suporte a esta funcionalidade, que apesar de muitos acharem ultrapassada, ainda é muito útil em diversas situações.

Acesso direto ao álbum de fotos

Todos nós que usamos o iPhoto sabemos como é útil ter um programa que acessa nossas fotos, as alteram e as organizam com base em reconhecimento de imagens e rostos. Até o OS 3.0 não era possível contruir um app assim, pois o único acesso que os desenvolvedores tinham às imagens da câmera era através de um ImagePicker, que é uma tela onde o usuário escolhe uma imagem e permite ao aplicativo acessá-la. Agora o acesso será direto, sem intermediários, permitindo uma nova gama de apps nesse sentido.

Acesso aos dados da câmera de vídeo em tempo real

Para mim é sem dúvida a maior revolução, em termos de API, do iPhone OS 4. O acesso ao vídeo permitirá que aplicativos reconheçam em tempo real os dados da câmera e que os manipulem, criando filtros da imagem, reconhecendo movimentos ou posicionando objetos virtuais na cena, tornando possível novos aplicativos de realidade aumentada.

Os aplicativos de realidade aumentada que existem hoje só podem usar a câmera como fundo e usam os dados de sensores, como GPS ou acelerômetro, para posicionar os objetos virtuais nesse fundo. Com essa nova API os desenvolvedores, além desses sensores, poderão utilizar técnicas de visão computacional para fazer reconhecimento de imagens e assim posicionar os objetos mais precisamente. Não entendeu? Veja o vídeo abaixo de um de inúmeros exemplos de realidade aumentada em dispositivos móveis:

Outro exemplo é o famoso Google googles, que já existe para Android.

Pra melhorar essa história ainda mais, a Apple anunciou a biblioteca Accelerate que disponibilizará filtros e algoritmos de manipulação de imagens eficientes para o iPhone. Um dos principais desafios da realidade aumentada para dispositivos móveis é exatamente a performance, manipular imagens a um frame rate razoável é complicado, vamos esperar que essa API da Apple possa nos ajudar nesse sentido.

Notificações locais

Quem não gostaria que o aplicativo avisasse dos nossos compromissos? Com o iPhone OS 3, aplicativos como o Pocket informant de gerenciamento de tarefas e calendários tinham que usar o push para nos avisar dos nossos compromissos, mas para isso era preciso ter conexão com a internet ilimitada, o que nem sempre ocorre, além de gastar mais bateria. Agora com o OS 4, podemos cadastrar eventos locais, o que permitirá que aplicativos como o Cine Mobits nos alerte do horário de um filme, ou o GloboEsporte do horário de um jogo específico.

Outras APIs

Se destacássemos todas, nosso post ficaria muito extenso, por isso o melhor é deixar uma referência para o site de desenvolvimento da Apple para os interessados em saber mais.

O importante é que as novas oportunidades estão aí e o limite agora está na imaginação dos desenvolvedores em criar novidades para seus aplicativos usando essas novas funcionalidades.

Leia também:

Podemos usar C++ com Objective-C?

Escrito por Karin em 10/03/10 14:07

Acredite, podemos sim! E como descobri isso? Durante esta semana, no desenvolvimento de um projeto, precisávamos de um algoritmo que convertesse uma imagem colorida em preto e branco. Após algumas pesquisas, encontramos o algoritmo do Chris Greening, porém o mesmo estava em C++. Pesquisando mais ainda, vimos que o compilador de Objective-C permite misturar C++ e Objective-C, porém com algumas ressalvas.

Para conseguir compilar código C++, o seu arquivo ".m" precisa ter a extensão ".mm". Mas atenção: os arquivos que tiverem tanto código C++ quanto Objective -C precisam também ter a extensão ".mm".

Observação: uma vez que seu código compile C++, código em C não é mais compilado. Para resolver esta questão, basta usar extern:

extern "C" {  #import "ImageUtils.h" }

Aqui vai um "Alo Mundo" que mistura tanto C++ quanto Objective-C como exemplo. Para testar, você pode colar o exemplo no main.mm.

#import <Foundation/Foundation.h> 

class AloMundo { 
    private: 
        id saudacao; 

    public: 
        AloMundo() { 
            saudacao = @"Alô, mundo!"; 
        } 

        AloMundo(const char* texto) { 
            saudacao = [[NSString alloc] initWithUTF8String:texto]; 
        } 

        void imprimir()  { 
            printf("%s\n", [saudacao UTF8String]); 
        } 
};

A classe AloMundo acima é em C++, porém se utiliza de código em Objective-C. Ela simplesmente tem a função de imprimir a saudação.

@interface Saudacao: NSObject { 
        AloMundo *aloMundo; 
}

- (id)init; 
- (void)dealloc; 
- (void)saudar; 
- (void)saudar:(AloMundo *)saudacao; 

@end 

@implementation Saudacao
- (id)init { 
    if (self = [super init])
        aloMundo = new AloMundo(); 

    return self; 
}

- (void)dealloc { 
    delete aloMundo; 
    [super dealloc]; 
} 

- (void)saudar { 
    aloMundo->imprimir(); 
} 

- (void)saudar:(AloMundo *)saudacao{ 
    saudacao->imprimir(); 
} 

@end

Já neste código acima, temos a classe Saudacao em Objective-C. Aqui também há código em C++, como podemos observar no init, que instancia a classe AloMundo.

int main() { 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    // Objective-C
    Saudacao *saudacao = [[Saudacao alloc] init]; 
    [saudacao saudar];                          // ------> Alô, mundo! 

    // C++
    AloMundo *aloMundo = new AloMundo("Hello, world!"); 
    [saudacao saudar:aloMundo];       // ------> Hello, world! 

    delete aloMundo; 
    [saudacao release]; 
    [pool release]; 

    return 0; 
}

No main, percebemos as duas formas de chamar o método de imprimir a saudação. Atenção para a forma de trabalhar com a memória. Em Objective-C, utilizamos alloc e release. Já em C++, new e delete.

Como mencionei acima, combinar as duas linguagens tem ressalvas, então tenha como premissas:

  • não é possível usar a sintaxe Objective-C para chamar um objeto C++;
  • não é possível adicionar construtores ou destrutores em Objective-C;
  • cuidado com as palavras reservadas de cada linguagem;
  • uma classe C++ não pode herdar de uma classe em Objective-C e vice-versa;
  • cada linguagem cuida de suas exceções, ou seja, uma exceção lançada em C++ não pode ser tratada em Objective-C e vice-versa.

Eu acredito que saber que podemos combinar as duas linguagens facilitará a vida de muitos desenvolvedores, pois já existem vários algoritmos prontos em C++ que são bastante úteis e não precisarão mais serem "traduzidos" para Objective-C.

Até mais!

Leia também:

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