Apostila iPhone – Distribuição AdHoc – Parte II

outubro 14th, 2010

Compilando aplicação com o Provisioning

Depois de efetuar o download deste provisioning, arraste este provisioning até o XCode. Isto fará com que o provisioning seja instalado no XCode automaticamente. Após esta instalação você precisa gerar uma versão de distribuição dentro do XCode para a sua aplicação. Selecione o nome do projeto e click em cima do botão “info” na barra de menu.

figura-65

Vá até a opção Configurations e duplique a versão Release. Para isso, de um click em cima dela e selecione Duplicate na parte de baixo da tela.

Após este passo, vamos relacionar o provisioning com a aplicação. Na pasta target, de um click no arquivo e novamente selecione “info” na barra de menu.

figura-66

Selecione a pasta Build e selecione o provisioning adequado (aquele que você arrastou para o XCode) na opção Any iPhone OS Device. Agora selecione a pasta Properties.

figura-67

Na opção Identifier você deve colocar exatamente o App ID que você criou no portal para a sua aplicação. Outra forma de fazer isso é modificar o arquivo info.plist.

figura-68

Repare que o nome descrito no App ID é o mesmo da aplicação, no caso myShow. Agora é só compilar e instalar sua aplicação nos devices que você cadastrou no Portal. Lembre-se que junto com a sua aplicação você deve instalar junto o provisioning, para tanto arraste tanto a aplicação como o provisioning para o iTunes. Assim que o iTunes sincronizar com o device ele irá instalar a sua aplicação. Existe um pequeno tutorial dentro do Portal que explica minuciosamente os passos para gerar cada tipo de distribuição.

Distribuição Apple Store

Para a distribuição via Apple Store, temos que fazer quase todos os procedimentos descritos anteriormente, a diferença está que ao invés de criar um provisioning do tipo AdHoc, nós criamos para Apple Store. Isto fará com que você não necessite informar em qual devices pretende instalar a sua aplicação visto que na verdade você deseja que qualquer device que acesse o iTunes possa fazer o download da sua aplicação.

Depois de fazer o download da seu provisioning você irá seguir os mesmos passos para compilar o seu novo provisioning com sua aplicação. A grande diferença está no fato de você ter que submeter sua aplicação depois de compilada no iTunes Connect.

(@Ademar Varela)

Apostile iPhone – Distribuição de Aplicações / Adhoc

outubro 2nd, 2010

Existe duas possibilidades de distribuir nossas aplicações, AdHoc e Apple Store. Porém vale lembrar que para qualquer uma delas é necessário ter uma licença de desenvolvimento. Esta licença você obtém no site da Apple e convém ler atentamente toda a documentação antes de entrar com as suas informções. Muitas pessoas me perguntam se o envio de informações delicadas, como cartão de crédito, é confiável. Eu sempre respondo que nunca ouvi falar que qualquer problema tenha ocorrido com estas informações. A própria Apple não tem interesse que profissionais ligados a ela através de uma plano de desenvolvimento sejam prejudicados de alguma forma. Mas vale cautela em qualquer operação financeira, e esta no final das contas não deixa de ser mais uma.

Antes de começarmos a explicação de como fazer uma licença de distribuição, é importante falar que antes de tudo é necessário criar um certificado para a máquina em que você está desenvolvendo. Estes passos estão descritos no Portal do Desenvolvedor, que você irá acessar assim que tiver a sua licença liberada.

Distribuição AdHoc

Uma das formas mais tradicionais para você testar a sua aplicação é utilizar uma distribuição do tipo AdHoc. Esta distribuição permite também que você distribua sua aplicação para um determinado número de devices (iPhones ou iPods Touch). Este número varia de acordo com a licença que você adquiriu. Mas vamos ao que interessa, como colocar uma aplicação em um device. Este tipo de distribuição é muito dependente do Portal do Desenvolvedor, ao contrário da distribuição pela Apple Store (que utiliza somente o essêncial do Portal).

iPhone Developer Program Portal

Vamos conhecer o Portal do Desenvolvedor. Como dito anteriormente, você só tem acesso ao portal depois que tem a sua licença liberada pela Apple. Para acessar o Portal, vá até o link: http://developer.apple.com/iphone/program/

figura-63

Na parte superior da página, existe um pequeno menu, selecione Dev Centers / iPhone Dev Center. Faça o login normalmente.

Neste Dev Center, você tem diversas informações, tais como referências, códigos fontes, downloads, fórum, ferramentas, etc.

Use este Dev Center, ele é muito útil no nosso dia a dia.

Do lado direito encontramos um pequeno menu, onde podemos selecionar:

    • iPhone Developer Program Portal;
    • iTunes Connect;
    • Apple Developer Forums;
    • Developer Support Center;
    • Marketing Resources.

De um click na primeira opção, iPhone Developer Program Portal.

figura-64

Do lado esquerdo temos outro pequeno menu com as opções, Home, Team, Certificates, Devices, App IDs, Provisioning e Distribution.

    • Home: Como o próprio nome diz, toda vez que você clicar nesta opção, a página que será exibida é exatamente esta tela inicial que estamos vendo neste momento. Geralmente encontramos algumas dicas interessantes nesta página, ela é dinâmica e muda com uma certa freqüência.
    • Team: É a página onde você irá informar quais são os desenvolvedores que fazer parte do seu time. Para quem usa uma licença individual, basta informar o nome do único desenvolvedor do seu time, no caso você mesmo.
    • Certificates: Como dito no início deste estudo, é necessário gerar um certificado da sua máquina de desenvolvimento. Depois de criado este certificado, você irá fazer um upload deste certificado para dentro do Portal. Depois de fazer este upload, a Apple vai gerar um arquivo de certificação que você irá fazer o download e instalar na sua máquina de desenvolvimento.
    • Devices: É a página aonde você irá cadastrar o Identificador do iPhone e/ou iPod Touch. Para conseguir mais facilmente este identificador, entre no iTunes com o device conectado e de um click em cima da opção Serial Number, isto fará com que o label mude para Identifier (UDID). Este número tem 40 dígitos e deve ser informado no momento que você estiver cadastrando o seu device (ou o device de um amigo).
    • App IDs: Nesta página vamos gerar um identificador para a nossa aplicação. Note, que este momento não é adequado para você ter uma crise artística e inventar um nome mirabolante para a sua aplicação. Isto deve ser feito quando você estiver codificando e o nome da aplicação que você irá informar aqui, deve ser exatamente igual ao nome que você utilizou para criar a sua aplicação. Quando falo exatamente, é exatamente, inclusive respeitando letras maiúsculas e minúsculas.
    • Provisioning: Esta é a página mais importante no processo de distribuição. É nela que vamos criar um arquivo do tipo provisioning que será usado na nossa aplicação. É nesta página que definimos se vamos criar uma distribuição AdHoc ou Apple Store. Se você escolher uma AdHoc, você irá informar em quais devices (aqueles que você cadastrou na opção Device) irão receber a aplicação.
    • Distribution: Nesta página temos diversas informações sobre distribuição de aplicações. Nesta página também temos um link direto com o iTunes Connect onde podemos colocar uma aplicação na Apple Store.

Não fique preocupado, cada uma dessas opções tem um pequeno tutorial que explica detalhadamente como você deve usar o Portal.

Como estamos estudando a forma de gerar uma distribuição AdHoc, vamos ter que passar por quase todas estas etapas que foram mostradas acima. Quando entrarmos na opção Provisioning, vamos escolher AdHoc e informar quais os devices que pretendemos rodar a nossa aplicação. Depois disso, o Portal vai liberar um download que é um provision que será usado na nossa aplicação.

(@AdemarVarela)

Apostila iPhone – Webservice

setembro 26th, 2010

Uma forma de acessar informações sem usar a base de dados do próprio iPhone é acessá-las através de um  webservice. Para fazermos este tipo de acesso é necessário criar uma estrutura XML e aplicar em um link hospedado em um servidor de dados.

Montando uma estrutura XML

Vamos supor que você precise acessar um webservice informando qual a sua localização de GPS, para isto vamos montar uma estrutura XML que receba os parâmetros de latitude e longitude.

NSString *soapMessage = [NSString stringWithFormat:

@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"

"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"

"<soap:Body>\n"

"<GetAllOffers xmlns=\"AcessoFacil.OfertaFacil\">\n"

"<latitude>%@</latitude>\n"

"<longitude>%@</longitude>\n"

"</GetAllOffers>"

"</soap:Body>\n"

"</soap:Envelope>\n",latitudeString, longitudeString];

Na estrutura acima, criamos uma variável do tipo NSString chamada soapMessage. Nesta variável, colocamos uma string que na verdade é uma estrutura XML, repare que deixamos nas tags de latitude e longitude a possibilidade de atribuição de conteúdo, pois usamos “%@”, no final da estrutura estamos informando a latitude e longitude. Claro que estas variáveis já foram carregadas da maneira correta e usando as premissas do uso do GPS.

Evocando o webservice

Depois de montada a estrutura XML que usaremos para usar no webservice, vamos executa-lo:

NSURL *url = [NSURL URLWithString:@"http://201.28.113.4/ofertafacil_dev/AcessoFacilOfertaFacil.asmx"];

NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];

NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];

[theRequest addValue: cookieSessionID forHTTPHeaderField:@"Cookie"];

[theRequest addValue: @"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

[theRequest addValue: @"AcessoFacil.OfertaFacil/GetAllOffers" forHTTPHeaderField:@"SOAPAction"];

[theRequest addValue: msgLength forHTTPHeaderField:@"Content-Length"];

[theRequest setHTTPMethod:@"POST"];

[theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];

NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

Primeiramente criamos uma variável do tipo URL, fazemos isto para poder criar um componente compatível com o necessário para acessar o serviço. É neste momento que vamos informar o link aonde contém o serviço que pretendemos usar. Depois criamos a requisição do serviço com o componente NSMutableURLRequest. Observe que na inicialização deste componente, estamos usando a URL anteriormente declarada.

Toda vez que precisamos executar um webservice, nós precisamos informar qual o tamanho da estrutura que vamos submeter, por esta razão precisamos definir o tamanho da estrutura com a função length. Depois disso, precisamos passar o que chamamos de cookies do serviço. Para terminar informamos qual o método que usaremos para executar o serviço, o corpo da requisição e por fim, fazemos a conexão.

Informando Headers

Em algumas situações precisamos acrescentar o que chamamos de header no webservice, para isso, antes de executar o serviço, precisamos usar o seguinte código:

NSString *headerCookie = @”ASP.NET_SessionId=”;

NSString *cookieSessionID = [NSString stringWithFormat:headerCookie];

cookieSessionID = [cookieSessionID stringByAppendingString:sessionID];

No exemplo acima, estamos criando uma variável do tipo NSString, que inicializamos com a variável que pretendemos passar pelo header do serviço. Depois é só concatenar o conteúdo da variável à string e executar no momento que chamamos o webservice:

[theRequest addValue: cookieSessionID forHTTPHeaderField:@"Cookie"];

(@AdemarVarela)

Apostila iPhone – Acelerômetro

setembro 20th, 2010

Vamos agora conhecer outro grande astro do mundo do iPhone, o acelerômetro. Esta implementação causou grande agito quando o iPhone foi lançado, e hoje com todas as operadoras implementando esta feature nos seus celulares, perdeu um pouco do encanto, mas continua com o seu charme.

Para usarmos esta feature, não é necessário nenhum grande conhecimento de Física ou Geometria. Para dizer a verdade é muito simples usar este conceito, pois na verdade a sua implementação já vem com o SDK, o que vamos fazer é apenas informar o que desejamos fazer quando movimentarmos o iPhone.

Para implementarmos o uso do acelerômetro na nossa programação basta implementarmos o seguinte método:

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {

}

Este método já está declarado n SDK e por esta razão não é necessário declara-lo novamente. Para configurarmos a tolerância do movimento que pretendemos dar à aplicação trabalhamos com a função fabsf. Passamos como parâmetro para esta função a aceleração que está sendo efetuada no aparelho. Se ajudar você, aceleração seria o quão brusco está sendo o movimento exercido no iPhone. Estes parâmetros também já estão configurados para os 3 eixos: x, y e z. No caso, os valores contidos em acceleration.x, acceleration.y e acceleration.z trazem a variação causada pelo usuário no iPhone.

Portanto, se você deseja configurar que uma determinada ação só ocorra caso o usuário faça um determinado movimento, você pode usar o seguinte código:

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {

if (fabsf(acceleration.x) > kAccelerationThreshold || fabsf(acceleration.y) > kAccelerationThreshold || fabsf(acceleration.z) > kAccelerationThreshold) {

….

}

}

Observem que dentro do método accelerometer, estamos limitando o movimento do usuário dentro do comando if. No caso estamos verificando se os eixos sofreram um movimento dentro do parâmetro kAccelerationThreshold. Neste caso específico, nós deveríamos ter definido o valor desta variável anteriormente, geralmente em um .h, por exemplo: #define kAccelerationThreshold 1.2. Completando o estudo, quanto menos este número mais sensível é o movimento captado pelo código.

(@AdemarVarela)

Apostila iPhone – GPS

setembro 16th, 2010

Agora vamos ver algo um pouco mais divertido, e que faz um grande sucesso no mundo do iPhone, o famoso GPS. Vamos lembrar porém, que no simulador, o GPS segue parâmetros fixos, pois o computador não possui GPS. Ele mantém estes valores fixos, apenas com a intenção de ajudar no nosso desenvolvimento.

Com o GPS podemos ter acesso às seguintes informações:

    • Ponto Inicial: Podemos marcar um ponto inicial do uso do GPS. Este ponto inicial pode ser útil caso precisemos saber qual uma distância percorrida, por exemplo.
    • Latitude: Como o nome diz, informa a Latitude da sua localização em graus.
    • Longitude: Adivinha? Informa a Longitude da sua localização em graus.
    • Altitude: Informa a Altitude que você se encontra.

Para usarmos o GPS do iPhone, é necessário acrescentar um framework no nosso projeto. No caso vamos adicionar o framework CoreLocation. Este framework já possui uma série de métodos prontos para serem usados, o que torna a nossa vida mais tranquila. Perceba que vamos chamar apenas métodos prontos para conseguir as nossas coordenadas GPS.

Antes de iniciarmos, vale uma informação importante. Quando o local aonde você se encontra não consegue receber o sinal GPS (isto ocorre muito em ambientes fechados), ele parte para o sistema de triangulação de antenas. Este sistema de triangulação não é uma localização precisa. Para manter um sistema preciso usando GPS mesmo, é necessário fazer algumas implementações para consistir a perda de sinal GPS e projetar a falta do mesmo. Mas isso poderemos aprender em outra ocasião.

Vamos criar um projeto chamado iGPS.

Adicionando o CoreLocation

Vamos agora adicionar o framework CoreLocation. Depois de criado o projeto, de um click na pasta Frameworks. Vá até o Menu Project / Add to Project:

figura-581

Ao escolher Add to Project, selecione o seguinte caminho:

    • HD;
    • Developer;
    • Platforms
    • iPhone Simulator;
    • Developer;
    • SDK;
    • iPhoneSimulator 3.0 (ou 3.1);
    • System;
    • Library;
    • Frameworks;

Ao chegar neste caminho selecione CoreLocation.framework.

No arquivo MainViewController.h, implemente o seguinte código:

#import “FlipsideViewController.h”

#import <CoreLocation/CoreLocation.h>

@interface MainViewController : UIViewController <FlipsideViewControllerDelegate, CLLocationManagerDelegate> {

CLLocationManager    *locationManager;

CLLocation           *startingPoint;

UILabel *latitudeLabel;

UILabel *longitudeLabel;

UILabel *horizontalAccuracyLabel;

UILabel *altitudeLabel;

UILabel *verticalAccuracyLabel;

UILabel *distanceTraveledLabel;

}

@property (retain, nonatomic) CLLocationManager *locationManager;

@property (retain, nonatomic) CLLocation *startingPoint;

@property (retain, nonatomic) IBOutlet UILabel *latitudeLabel;

@property (retain, nonatomic) IBOutlet UILabel *longitudeLabel;

@property (retain, nonatomic) IBOutlet UILabel *horizontalAccuracyLabel;

@property (retain, nonatomic) IBOutlet UILabel *altitudeLabel;

@property (retain, nonatomic) IBOutlet UILabel *verticalAccuracyLabel;

@property (retain, nonatomic) IBOutlet UILabel *distanceTraveledLabel;

- (IBAction)showInfo;

@end

Observe que estamos importando o framework CoreLocation na segunda linha, e na declaração da interface existe uma delegação CLLocationManagerDelegate. Na declaração das variáveis foram usados dois tipos de componentes específicos do novo framework, CLLocationManager e CLLocation. Eles serão a base para montar os cálculos do GPS.

No arquivo MainViewController.m, implemente o seguinte código:

#import “MainViewController.h”

#import “MainView.h”

@implementation MainViewController

@synthesize locationManager;

@synthesize startingPoint;

@synthesize latitudeLabel;

@synthesize longitudeLabel;

@synthesize horizontalAccuracyLabel;

@synthesize altitudeLabel;

@synthesize verticalAccuracyLabel;

@synthesize distanceTraveledLabel;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

// Custom initialization

}

return self;

}

- (void)viewDidLoad {

self.locationManager = [[CLLocationManager alloc] init];

locationManager.delegate = self;

locationManager.desiredAccuracy = kCLLocationAccuracyBest;

[locationManager startUpdatingLocation];

}

- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller {

[self dismissModalViewControllerAnimated:YES];

}

- (IBAction)showInfo {

FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@”FlipsideView” bundle:nil];

controller.delegate = self;

controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

[self presentModalViewController:controller animated:YES];

[controller release];

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

}

- (void)viewDidUnload {

self.locationManager = nil;

self.latitudeLabel = nil;

self.longitudeLabel = nil;

self.horizontalAccuracyLabel = nil;

self.altitudeLabel = nil;

self.verticalAccuracyLabel = nil;

self.distanceTraveledLabel = nil;

[super viewDidUnload];

}

- (void)dealloc {

[locationManager release];

[startingPoint release];

[latitudeLabel release];

[longitudeLabel release];

[horizontalAccuracyLabel release];

[altitudeLabel release];

[verticalAccuracyLabel release];

[distanceTraveledLabel release];

[super dealloc];

}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {

if (startingPoint == nil)

self.startingPoint = newLocation;

NSString *latitudeString = [[NSString alloc] initWithFormat:@”%g°”, newLocation.coordinate.latitude];

latitudeLabel.text = latitudeString;

[latitudeString release];

NSString *longitudeString = [[NSString alloc] initWithFormat:@”%g°”, newLocation.coordinate.longitude];

longitudeLabel.text = longitudeString;

[longitudeString release];

NSString *horizontalAccuracyString = [[NSString alloc] initWithFormat:@”%gm”, newLocation.horizontalAccuracy];

horizontalAccuracyLabel.text = horizontalAccuracyString;

[horizontalAccuracyString release];

NSString *altitudeString = [[NSString alloc] initWithFormat:@”%gm”, newLocation.altitude];

altitudeLabel.text = altitudeString;

[altitudeString release];

NSString *verticalAccuracyString = [[NSString alloc] initWithFormat:@”%gm”, newLocation.verticalAccuracy];

verticalAccuracyLabel.text = verticalAccuracyString;

[verticalAccuracyString release];

CLLocationDistance distance = [newLocation  getDistanceFrom:startingPoint];

NSString *distanceString = [[NSString alloc] initWithFormat:@”%gm”, distance];

distanceTraveledLabel.text = distanceString;

[distanceString release];

}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {

NSString *errorType = (error.code == kCLErrorDenied) ? @”Access Denied” : @”Unknown Error”;

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”Error getting Location” message:errorType delegate:nil cancelButtonTitle:@”Okay” otherButtonTitles:nil];

[alert show];

[alert release];

}

@end

Observe no código que existem variáveis pré definidas, que aparentemente não declaramos, por exemplo kCLLocationAccuracyBest. Na verdade esta variável está declarada no framework CoreLocation e por isto não precisamos declarar explicidamente no nosso código.

Rode o aplicativo e divirta-se.

(@AdemarVarela)



Apostila iPhone – Persistência de Dados – Archiving

setembro 12th, 2010

A última forma de persistir uma informação é usando archiving. Na verdade vamos criar um arquivo binário para armazenar nossas informações. A forma de gravar informações é muito similar com o que acabamos de ver no SQLite3.

Vamos criar um projeto chamado iArch, para estudar este formato de gravação.

Da mesma forma que criamos uma classe para controlar nossa base de dados, vamos criar uma classe com o mesmo propósito neste projeto. Crie uma classe chamada Fields.

figura-62

No arquivo Fields.h, digite o seguinte código:

#define    kField1Key    @“Field1″

#define    kField2Key    @“Field2″

#define    kField3Key    @“Field3″

#define    kField4Key    @“Field4″

@interface Fields : NSObject <NSCoding, NSCopying> {

NSString *field1;

NSString *field2;

NSString *field3;

NSString *field4;

}

@property (nonatomic, retain) NSString *field1;

@property (nonatomic, retain) NSString *field2;

@property (nonatomic, retain) NSString *field3;

@property (nonatomic, retain) NSString *field4;

@end

Como podemos observar, nada diferente do que já havíamos feito no exemplo do SQLite3. No arquivo Fields.m, digite o seguinte código:

#import “Fields.h”

@implementation Fields

@synthesize field1;

@synthesize field2;

@synthesize field3;

@synthesize field4;

- (void)encodeWithCoder:(NSCoder *)encoder {

[encoder encodeObject:field1 forKey:kField1Key];

[encoder encodeObject:field2 forKey:kField2Key];

[encoder encodeObject:field3 forKey:kField3Key];

[encoder encodeObject:field4 forKey:kField4Key];

}

- (id)initWithCoder:(NSCoder *)decoder {

if (self = [super init]) {

self.field1 = [decoder decodeObjectForKey:kField1Key];

self.field2 = [decoder decodeObjectForKey:kField2Key];

self.field3 = [decoder decodeObjectForKey:kField3Key];

self.field4 = [decoder decodeObjectForKey:kField4Key];

}

return self;

}

- (id)copyWithZone:(NSZone *)zone {

Fields *copy = [[[self class] allocWithZone: zone] init];

copy.field1 = [[self.field1 copyWithZone:zone] autorelease];

copy.field2 = [[self.field2 copyWithZone:zone] autorelease];

copy.field3 = [[self.field3 copyWithZone:zone] autorelease];

copy.field4 = [[self.field4 copyWithZone:zone] autorelease];

return copy;

}

@end

Percebemos que esta classe, assim como a anterior é idêntica a classe criada no estudo anterior. Para facilitar a compreensão usaremos o mesmo esquema proposto nos estudos anteriores.

No arquivo MainViewController.h, digite o seguinte código:

#import “FlipsideViewController.h”

#define kFilename        @“archive”

#define kDataKey         @“Data”

@interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {

UITextField *field1;

UITextField *field2;

UITextField *field3;

UITextField *field4;

}

@property (nonatomic, retain) IBOutlet UITextField *field1;

@property (nonatomic, retain) IBOutlet UITextField *field2;

@property (nonatomic, retain) IBOutlet UITextField *field3;

@property (nonatomic, retain) IBOutlet UITextField *field4;

- (NSString *)dataFilePath;

- (void)applicationWillTerminate:(NSNotification *)notification;

- (IBAction)showInfo;

@end

Aqui encontramos a primeira diferença do estudo anterior e deste atual, observe os defines que estamos usando agora, kFilename determina o nome do arquivo, já kDataKey a chave que será usada para manipular este arquivo. Lembre-se, vamos criar um arquivo binário e por esta razão vamos precisar de uma chave para poder navegar por ele.

No arquivo MainViewController.m, digite o seguinte código:

#import “MainViewController.h”

#import “MainView.h”

#import “Fields.h”

@implementation MainViewController

@synthesize field1;

@synthesize field2;

@synthesize field3;

@synthesize field4;

- (NSString *)dataFilePath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(

NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

return [documentsDirectory stringByAppendingPathComponent:kFilename];

}

- (void)applicationWillTerminate: (NSNotification *)notification {

Fields *fourLines = [[Fields alloc] init];

fourLines.field1 = field1.text;

fourLines.field2 = field2.text;

fourLines.field3 = field3.text;

fourLines.field4 = field4.text;

NSMutableData *data = [[NSMutableData alloc] init];

NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]

initForWritingWithMutableData:data];

[archiver encodeObject:fourLines forKey:kDataKey];

[archiver finishEncoding];

[data writeToFile:[self dataFilePath] atomically:YES];

[fourLines release];

[archiver release];

[data release];

}

- (void)viewDidLoad {

NSString *filePath = [self dataFilePath];

if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {

NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];

NSData *data = [[NSMutableData alloc]

initWithContentsOfFile:[self dataFilePath]];

NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]

initForReadingWithData:data];

Fields *fourLines = [unarchiver decodeObjectForKey:kDataKey];

[unarchiver finishDecoding];

field1.text = fourLines.field1;

field2.text = fourLines.field2;

field3.text = fourLines.field3;

field4.text = fourLines.field4;

[unarchiver release];

[data release];

}

UIApplication *app = [UIApplication sharedApplication];

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(applicationWillTerminate: )

name:UIApplicationWillTerminateNotification

object:app];

[super viewDidLoad];

}

- (void)viewDidUnload {

self.field1 = nil;

self.field2 = nil;

self.field3 = nil;

self.field4 = nil;

[super viewDidUnload];

}

- (void)dealloc {

[field1 release];

[field2 release];

[field3 release];

[field4 release];

[super dealloc];

}

@end

Observem que os métodos que criamos são os mesmo usados no estudo anterior, a grande diferença está somente nos métodos viewDidLoad e appicationWillTerminate. Nestes métodos estamos usando comandos específicos para gravar informações usando archiving, tais como, NSKeyedArchiver e NSFileManager.

(@AdemarVarela)

Apostila iPhone – Persistência de Dados – SQLite – Final

setembro 12th, 2010

Abra o arquivo MainView.xib e insira os componentes como mostrado na figura abaixo:

figura-61

Agora feche o IB e rode o aplicativo, e claro, divirta-se.

Exercício

  1. Modifique o aplicativo iAgenda para trabalhar com SQLite3.

(@AdemarVarela)

Apostila iPhone – Persistência de Dados – SQLite

setembro 8th, 2010

Outra forma de persistir informações é utilizar o SQLite. O SQLite é um pequeno banco de dados, que responde aos comandos SQL ANSI, tais como select, insert, update, etc.

Basicamente, a forma de persistir é muito similar à Property List.

Vamos criar um projeto para conhecer o SQLite, chamado iSQL. Para trabalharmos com SQLite é necessário acrescentar uma nova biblioteca no projeto, libsqlite3.dylib. Esta é uma biblioteca que chamamos de dinâmica, por isto a extensão dylib.

Para acrescentar esta biblioteca, de um click em cima da pasta Framework, depois selecione Project / Add to Project.

figura-58

Ao escolher Add to Project, selecione o seguinte caminho:

    • HD;
    • Developer;
    • Platforms;
    • iPhoneSimulator.platform;
    • Developer;
    • SDKs;
    • iPhoneSimulator3.0 (ou 3.1);
    • usr;
    • lib;

Ao chegar neste caminho selecione libsqlite3.dylib.

Geralmente em projetos com SQLite, nós criamos uma classe que determina o modelo dos dados que serão gravados. Portanto vamos criar uma classe denominada Fields que conterá o modelo de dados que será gravado.

figura-60

Implemente o seguinte código em Fields.h:

#import <Foundation/Foundation.h>

#define    kField1Key    @“Field1″

#define    kField2Key    @“Field2″

#define    kField3Key    @“Field3″

#define    kField4Key    @“Field4″

@interface Fields :  NSObject <NSCoding, NSCopying> {

NSString *field1;

NSString *field2;

NSString *field3;

NSString *field4;

}

@property (nonatomic, retain) NSString *field1;

@property (nonatomic, retain) NSString *field2;

@property (nonatomic, retain) NSString *field3;

@property (nonatomic, retain) NSString *field4;

@end

Observem que estamos definindo 4 campos que serão utilizados para a manipulação da nossa base de dados.

No arquivo Fields.m, implemente o seguinte código:

#import “Fields.h”

@implementation Fields

@synthesize field1;

@synthesize field2;

@synthesize field3;

@synthesize field4;

- (void)encodeWithCoder:(NSCoder *)encoder {

[encoder encodeObject:field1 forKey:kField1Key];

[encoder encodeObject:field2 forKey:kField2Key];

[encoder encodeObject:field3 forKey:kField3Key];

[encoder encodeObject:field4 forKey:kField4Key];

}

- (id)initWithCoder:(NSCoder *)decoder {

if (self = [super init]) {

self.field1 = [decoder decodeObjectForKey:kField1Key];

self.field2 = [decoder decodeObjectForKey:kField2Key];

self.field3 = [decoder decodeObjectForKey:kField3Key];

self.field4 = [decoder decodeObjectForKey:kField4Key];

}

return self;

}

- (id)copyWithZone:(NSZone *)zone {

Fields *copy = [[[self class] allocWithZone: zone] init];

copy.field1 = [[self.field1 copyWithZone:zone] autorelease];

copy.field2 = [[self.field2 copyWithZone:zone] autorelease];

copy.field3 = [[self.field3 copyWithZone:zone] autorelease];

copy.field4 = [[self.field4 copyWithZone:zone] autorelease];

return copy;

}

@end

Criada a classe que manipulará a base de dados, vamos implementar o código propriamente dito. Vamos perceber que a implementação não foge muito daquilo que foi feito para a persistência do Property List. No arquivo MainViewController.h, implemente o seguinte código:

#import “FlipsideViewController.h”

#import “/usr/include/sqlite3.h”

#define kFilename    @“data.sqlite3″

@interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {

UITextField *field1;

UITextField *field2;

UITextField *field3;

UITextField *field4;

sqlite3 *database;

}

@property (nonatomic, retain) IBOutlet UITextField *field1;

@property (nonatomic, retain) IBOutlet UITextField *field2;

@property (nonatomic, retain) IBOutlet UITextField *field3;

@property (nonatomic, retain) IBOutlet UITextField *field4;

- (NSString *)dataFilePath;

- (void)applicationWillTerminate: (NSNotification *)notification;

- (IBAction)showInfo;

@end

Observe que colocamos um define que determina o nome da base de dados, no nosso caso data.sqlite3. Declaramos uma variável chamada database como sendo do tipo slqlite3.

No arquivo MainViewController.m, implemente o seguinte código:

#import “MainViewController.h”

#import “MainView.h”

#import “Fields.h”

@implementation MainViewController

@synthesize field1;

@synthesize field2;

@synthesize field3;

@synthesize field4;

- (NSString *)dataFilePath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(

NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

return [documentsDirectory stringByAppendingPathComponent:kFilename];

}

- (void)applicationWillTerminate:(NSNotification *)notification {

for (int i = 1; i <= 4; i++)

{

NSString *fieldName = [[NSString alloc]

initWithFormat:@”field%d”, i];

UITextField *field = [self valueForKey:fieldName];

[fieldName release];

char *errorMsg;

char *update = “INSERT OR REPLACE INTO FIELDS (ROW, FIELD_DATA) VALUES (?, ?);”;

sqlite3_stmt *stmt;

if (sqlite3_prepare_v2(database, update, -1, &stmt, nil) == SQLITE_OK) {

sqlite3_bind_int(stmt, 1, i);

sqlite3_bind_text(stmt, 2, [field.text UTF8String], -1, NULL);

}

if (sqlite3_step(stmt) != SQLITE_DONE)

NSAssert1(0, @”Error updating table: %s”, errorMsg);

sqlite3_finalize(stmt);

}

sqlite3_close(database);

}

- (void)viewDidLoad {

if (sqlite3_open([[self dataFilePath] UTF8String], &database)

!= SQLITE_OK) {

sqlite3_close(database);

NSAssert(0, @”Failed to open database”);

}

char *errorMsg;

NSString *createSQL = @”CREATE TABLE IF NOT EXISTS FIELDS (ROW INTEGER PRIMARY KEY, FIELD_DATA TEXT);”;

if (sqlite3_exec (database, [createSQL UTF8String],

NULL, NULL, &errorMsg) != SQLITE_OK) {

sqlite3_close(database);

NSAssert1(0, @”Error creating table: %s”, errorMsg);

}

NSString *query = @”SELECT ROW, FIELD_DATA FROM FIELDS ORDER BY ROW”;

sqlite3_stmt *statement;

if (sqlite3_prepare_v2( database, [query UTF8String],

-1, &statement, nil) == SQLITE_OK) {

while (sqlite3_step(statement) == SQLITE_ROW) {

int row = sqlite3_column_int(statement, 0);

char *rowData = (char *)sqlite3_column_text(statement, 1);

NSString *fieldName = [[NSString alloc]

initWithFormat:@”field%d”, row];

NSString *fieldValue = [[NSString alloc]

initWithUTF8String:rowData];

UITextField *field = [self valueForKey:fieldName];

field.text = fieldValue;

[fieldName release];

[fieldValue release];

}

sqlite3_finalize(statement);

}

UIApplication *app = [UIApplication sharedApplication];

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(applicationWillTerminate: )

name:UIApplicationWillTerminateNotification

object:app];

[super viewDidLoad];

}

- (void)viewDidUnload {

// Release any retained subviews of the main view.

// e.g. self.myOutlet = nil;

self.field1 = nil;

self.field2 = nil;

self.field3 = nil;

self.field4 = nil;

[super viewDidUnload];

}

- (void)dealloc {

[field1 release];

[field2 release];

[field3 release];

[field4 release];

[super dealloc];

}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

// Custom initialization

}

return self;

}

- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller {

[self dismissModalViewControllerAnimated:YES];

}

- (IBAction)showInfo {

FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@”FlipsideView” bundle:nil];

controller.delegate = self;

controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

[self presentModalViewController:controller animated:YES];

[controller release];

}

- (void)didReceiveMemoryWarning {

// Releases the view if it doesn’t have a superview.

[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren’t in use.

}

@end

Reparem que as grandes modificações ocorreram nos métodos viewDidLoad e aplicationWillTerminate, o restante se manteve muito próximo do exemplo do Property List.

(@AdemarVarela)

Pesquisa da Lockerz

setembro 6th, 2010

Para quem tem Lockerz, veja a http://www.lockerz.com/dailies do dia 4 de Setembro…

Para quem não tem, disponibilizamos a tela com o resultado da pesquisa feita por eles.

image001

Apostila iPhone – Persistência de Dados

setembro 6th, 2010

Chegamos em um ponto interessante do nosso treinamento. Já conseguimos manipular vários elementos de interface, agora vamos aprender como manipular dados de persistentes. O que são dados persistentes? São informações que pretendemos armazenar em algum formato para posterior consulta. Dizemos que persistimos estas informações para garantir a integridade da informação, por exemplo, não adianta guardar uma informação sobre um determinado telefone que contenha letras, visto que pelo menos por enquanto só usamos números para isso.

Property List

O primeiro tipo de persistência que vamos aprender é o chamado de Property List. Uma Property List é na verdade um arquivo no formato XML que permite alterar o seu conteúdo dinamicamente. Já estamos manipulando há algum tempo um arquivo do tipo Property List. Toda vez que usamos o info.plist do projeto, estamos manipulando um arquivo Property List (por isto a extensão plist). Repare que podemos acrescentar qualquer parâmetro neste tipo de arquivo. Já fizemos um execício anteriormente usando um arquivo no formato XML que na verdade estávamos tratando como sendo um plist.

Como de costume, vamos criar um projeto para fazermos os conceitos básicos de persistência de uma plist. Crie um projeto chamado iPList. Mantendo nossas diretrizes, vamos criar um projeto do tipo Utility e sem a barra de status. Sempre fazemos isto para reforçar nossos conceitos aprendidos.

Abra o resource MainView.xib, e vamos acrescentar alguns componentes do tipo UITextField. Na verdade vamos acrescentar 4 campos deste tipo. Para facilitar a nossa compreensão vamos colocar 4 componentes do tipo UILabel.

figura-57

Feche o IB e retorne para o XCode.

No arquivo MainViewController.h, inclua o seguinte código:

#import “FlipsideViewController.h”

#define kFilename        @“data.plist”

@interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {

UITextField *field1;

UITextField *field2;

UITextField *field3;

UITextField *field4;

}

@property (nonatomic, retain) IBOutlet UITextField *field1;

@property (nonatomic, retain) IBOutlet UITextField *field2;

@property (nonatomic, retain) IBOutlet UITextField *field3;

@property (nonatomic, retain) IBOutlet UITextField *field4;

- (NSString *)dataFilePath;

- (void)applicationWillTerminate:(NSNotification *)notification;

- (IBAction)showInfo;

@end

Vamos analisar o código digitado.

Logo na segunda linha temos um define. É neste define que estamos definindo o nome do arquivo XML que conterá as informações que digitaremos, no nosso caso, as informações que estarão contidas em field1, field2, field3 e field4.

A propósito, se vocês repararem bem, acabamos de usar uma forma diferente de declarar as variáveis do tipo IB. Observem que colocamos a declaração IBOutlet na declaração do property. Isto é permitido também.

Criamos também duas funções, que terão o seguinte objetivo:

    • dataFilePath: é uma função que retornará o caminho que deverá conter o arquivo XML no formato plist.
    • applicationWillTerminate: É um método que será chamado no momento que a aplicação estiver sendo encerrado.

No arquivo MainViewController.m, digite o seguinte código:

#import “MainViewController.h”

#import “MainView.h”

@implementation MainViewController

@synthesize field1;

@synthesize field2;

@synthesize field3;

@synthesize field4;

- (NSString *)dataFilePath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

return [documentsDirectory stringByAppendingPathComponent:kFilename];

}

- (void)applicationWillTerminate:(NSNotification *)notification {

NSMutableArray *array = [[NSMutableArray alloc] init];

[array addObject:field1.text];

[array addObject:field2.text];

[array addObject:field3.text];

[array addObject:field4.text];

[array writeToFile:[self dataFilePath] atomically:YES];

[array release];

}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

// Custom initialization

}

return self;

}

- (void)viewDidLoad {

NSString *filePath = [self dataFilePath];

if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {

NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];

field1.text = [array objectAtIndex:0];

field2.text = [array objectAtIndex:1];

field3.text = [array objectAtIndex:2];

field4.text = [array objectAtIndex:3];

[array release];

}

UIApplication *app = [UIApplication sharedApplication];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate: ) name:UIApplicationWillTerminateNotification object:app];

[super viewDidLoad];

}

- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller {

[self dismissModalViewControllerAnimated:YES];

}

- (IBAction)showInfo {

FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@”FlipsideView” bundle:nil];

controller.delegate = self;

controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

[self presentModalViewController:controller animated:YES];

[controller release];

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

}

- (void)viewDidUnload {

self.field1 = nil;

self.field2 = nil;

self.field3 = nil;

self.field4 = nil;

[super viewDidUnload];

}

- (void)dealloc {

[field1 release];

[field2 release];

[field3 release];

[field4 release];

[super dealloc];

}

@end

Podemos observar que quase todos os comandos digitados já foram utilizados exaustivamente por nós. Eu disse quase todos, porque temos dois que ainda não vimos, UIApplication e NSNotificationCenter.

Usamos o UIApplication para instanciar uma aplicação. No nosso caso, estamos instanciando uma variável (app) com a própria aplicação que estamos executando. Usaremos esta variável no momento que formos informar a aplicação, que existe uma notificação de encerramento para esta aplicação. Quando ocorrer o encerramento da aplicação, ocorrerá uma notificação chamada UIApplicationWillTerminateNotification, que irá disparar a execução do método applicationWillTerminate. É isto que o comando NSNotificationCenter faz, informa o que será executado no momento que uma notificação ocorrer. Chamamos este tipo de intervenção, de execução de método condicionado. Um determinado método, só está sendo executado mediante uma condição específica que ocorreu através de uma ação do usuário.

Exercício

1. Crie um projeto chamado iAgenda. Grave em um plist chamado “agenda.plist” o nome, o endereço e o telefone. Consista as informações, grave somente se todos os campos possuirem conteúdo.

(@AdemarVarela)