Eu vejo muitas pessoas
perguntando sobre material de SQLite por aí, então pensei em escrever um artigo sobre o uso do SQLite com o iPhone SDK.
1. Requisitos do projeto
Parto do pressuposto de que você
tenha pelo menos um conhecimento básico de SQLite, escrita de declarações SQL,
interface, XCode e uso do terminal no OSX. Se você não sabe nada sobre nenhum
desses tópicos, provavelmente este tutorial não é para você.
2. Criando nosso banco de dados SQLite
Primeiramente, precisamos criar um banco de dados para usarmos com nosso aplicativo. Para o objetivo deste artigo,
iremos criar um banco de dados de animais com algumas informações sobre eles e uma
imagem.
Abra uma nova janela no Terminal e crie uma nova
pasta para conter o banco de dados; aqui estão os comandos que executei:
cd /Users/lookaflyingdonkey/Documents
mkdir SQLiteTutorial
cd SQLiteTutorial
sqlite3 AnimalDatabase.sql
Nesse ponto, você deve estar no
prompt de comando “sqlite”, onde nós construiremos nossa estrutura do banco de dados
e colocaremos alguns dados de teste.
No nosso exemplo, precisamos do
nome do animal, de uma pequena discrição e de um link para a imagem. Siga os comandos
abaixo e crie uma
tabela e insira alguns dados de exemplo.
CREATE TABLE animals ( id INTEGER PRIMARY KEY, name VARCHAR(50), description TEXT, image VARCHAR(255) );
INSERT INTO animals (name, description, image) VALUES ('Elephant', 'The elephant is a very large animal that lives in Africa and Asia', 'http://dblog.com.au/wp-content/elephant.jpg');
INSERT INTO animals (name, description, image) VALUES ('Monkey', 'Monkies can be VERY naughty and often steal clothing from unsuspecting tourists', 'http://dblog.com.au/wp-content/monkey.jpg');
INSERT INTO animals (name, description, image) VALUES ('Galah', 'Galahs are a wonderful bird and they make a great pet (I should know, I have one)', 'http://dblog.com.au/wp-content/galah.jpg');
INSERT INTO animals (name, description, image) VALUES ('Kangaroo', 'Well I had to add the Kangaroo as they are the essence of the Australian image', 'http://dblog.com.au/wp-content/kangaroo.jpg');
O primeiro comando criará a
tabela com a estrutura necessária, e os próximos quatro irão inserir alguns
dados de teste para podermos trabalhar. Para garantir que você tenha inserido
os dados corretamente, você pode executar “SELECT * FROM animals;” e ver se ele
retorna os itens acima. Uma vez que você estiver confiante de que tudo foi criado
com sucesso, pode deixar a linha de comando sqlite ao digitar “.quit”.
3. Criando nosso projeto
Agora que nosso dados está
pronto, precisamos configurar nosso projeto X-Code.
Comece criando um novo “Navigation-Based
Application”.
Dê um nome para seu projeto, eu
chamei o meu de “SQLiteTutorial”.
Agora configure o layout da sua
tela da maneira que você prefere, sugiro deixar a janela o maior possível, e a
visualização do código o mais alto possível ao puxar o slider horizontal para o
topo. Isso te dará mais espaço para trabalhar ao construir seu aplicativo.
Agora é hora de criar as classes
necessárias e as visualizações de nosso aplicativo, e começaremos fazendo nossas
visualizações.
Clique com o botão direito na
pasta “Resources” no painel da esquerda e clique em “Add File”; queremos criar um
novo “View XIB” abaixo do grupo “User Interfaces”.
Agora precisamos nomeá-lo e, para
manter as convenções de nome da Apple, iremos chamá-lo de “AnimalViewController.xib”. Em seguida, clique em “Finish”.
Agora precisamos criar duas
classes; a primeira representará um animal. Clique com o botão direito na pasta
“Classes” no painel esquerdo, clique em Add > New File…”, escolha o template
“NSObject subclass” sob o grupo “Cocoa
Touch Classes” e o chame de “Animal”.
A segunda classe será para nosso AnimalsViewController. Clique com o botão direito na pasta “Classes” no painel esquerdo, clique em
“Add > New File…”, e escolha o “UIViewController subclass” sob o grupo “Cocoa Touch Classes” e o chame de “AnimalViewController”.
4. Adicionando um framework
SQLite e nosso banco de dados de animais
Agora que criamos todas nossas
visualizações e classes, é hora de começar o trabalho de verdade.
Primeiramente, temos que incluir
as bibliotecas do SQLite de modo que nosso aplicativo possa utilizá-las. Para
fazer isso, você precisa clicar com o botão direito na pasta “Frameworks” no
painel esquerdo, então clique em “Add > Existing Frameworks…”, e em seguida
navegue para
“/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/usr/lib/”
e dê um clique duplo na pasta “libsqlite3.0.dylib”. Um popup vai aparecer,
simplesmente clique em “Add”, e a biblioteca será adicionada no seu projeto.
Também precisamos adicionar o banco de dados que criamos mais cedo na pasta Resources e, para fazer isso, clique
com o botão direito na pasta “Resources”, clique em “Add > Existing Files…”, navegue para o local
onde você criou o banco de dados e então dê um duplo clique no arquivo AnimalDatabase.sql
file. Outro popup aparecerá, apenas clique em “add”.
A importação está pronta, hora de
codificar!
5. A codificação começa!
Vamos começar nossa codificação
construindo nosso objeto “Animal”. Cada animal terá três propriedades, um nome,
uma descrição e uma imagem URL.
Abra o arquivo “Animal.h” da pasta “Classes” e
edite seu conteúdo para que ele se pareça com o mostrado abaixo:
#import <UIKit/UIKit.h>
@interface Animal : NSObject {
NSString *name;
NSString *description;
NSString *imageURL;
}
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *description;
@property (nonatomic, retain) NSString *imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u;
@end
A maior parte do código acima
deve ser bastante familiar para você, a única coisa que talvez não seja é a
linha initWithName. Essa linha nos permite criar um novo objeto com os
dados requeridos; nós poderíamos ter usado a função padrão init, mas
será mais fácil para nós definirmos a nossa linha própria.
Agora iremos implementar o objeto Animal de
fato. Abra o arquivo “Animal.m” e edite seu conteúdo para que ele se pareça com
o mostrado abaixo:
#import "Animal.h"
@implementation Animal
@synthesize name, description, imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
self.name = n;
self.description = d;
self.imageURL = u;
return self;
}
@end
O código acima deve ser bem fácil
de ler também, ele basicamente armazena os dados fornecidos pela função initWithName
e retorna o objeto (self).
Agora é hora de configurar o “Application delegate”
para acessar o banco de dados.
#import <UIKit/UIKit.h>
#import <sqlite3.h> // Import the SQLite database framework
@interface SQLiteTutorialAppDelegate : NSObject {
UIWindow *window;
UINavigationController *navigationController;
// Database variables
NSString *databaseName;
NSString *databasePath;
// Array to store the animal objects
NSMutableArray *animals;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
@property (nonatomic, retain) NSMutableArray *animals;
@end
O que estamos fazendo aqui é
importando o framework do banco de dados SQLite e criando algumas variáveis para
armazenar os detalhes do banco e um array
de objetos “Animal”.
Agora abra o arquivo “SQLiteTutorialAppDelegate.m”
e edite seu conteúdo para que ele se pareça com o mostrado abaixo:
#import "SQLiteTutorialAppDelegate.h"
#import "RootViewController.h"
#import "Animal.h" // Import the animal object header
@implementation SQLiteTutorialAppDelegate
@synthesize window;
@synthesize navigationController;
@synthesize animals; // Synthesize the aminals array
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Setup some globals
databaseName = @"AnimalDatabase.sql";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// Execute the "checkAndCreateDatabase" function
[self checkAndCreateDatabase];
// Query the database for all animal records and construct the "animals" array
[self readAnimalsFromDatabase];
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Save data if appropriate
}
- (void)dealloc {
[animals release];
[navigationController release];
[window release];
[super dealloc];
}
-(void) checkAndCreateDatabase{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success) return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
[fileManager release];
}
-(void) readAnimalsFromDatabase {
// Setup the database object
sqlite3 *database;
// Init the animals Array
animals = [[NSMutableArray alloc] init];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from animals";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aDescription = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *aImageUrl = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
// Create a new animal object with the data from the database
Animal *animal = [[Animal alloc] initWithName:aName description:aDescription url:aImageUrl];
// Add the animal object to the animals Array
[animals addObject:animal];
[animal release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
@end
Sei que isso pode parecer muito
código, e ele realmente parece assustador! Mas é, na verdade, bem simples, e eu
tentei comentar quase toda linha para mostrar a você o que cada linha faz e
por que está ali.
A função
checkAndCreateDatabase checa para ver
se já copiamos nosso banco de dados do pacote de aplicativos para o sandbox (dentro da pasta “documents”), se o banco de dados já foi criado, ou se
foi removido por alguma razão e, se sim, será recriado a partir do banco de dados
padrão.
Em seguida, a função readAnimalsFromDatabase “irá fazer uma conexão com a database que está armazenada na pasta “users
documents”, e então executará a declaração “SELECT * FROM animals” no SQL.
Então, ela irá percorrer cada linha retornada e extrairá o nome, descrição e
imagem URL do resultado, e construir um objeto Animal para cada uma delas. Você verá a função sqlite3_column_text sendo
usada aqui. Existem muitas outras funções como essa para retornar outros tipos
de arquivo, como sqlite3_column_int para números inteiros, sqlite3_column_blob para blobs ou sqlite3_column_value para
ter um valor desconhecido.
Agora que temos os dados no nosso
array, e sabemos seu formato, estamos
prontos para começar a exibi-los.
Abra o arquivo “RootViewController.m” e
edite o numberOfRowsInSection para que fique parecido com
este:
SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
return appDelegate.animals.count;
O que ele faz é criar um link para
o “application delegate”, e então a segunda linha retorna
o tamanho dos arrays dos animais no
nosso “Application delegate” – esse array
foi preenchido anteriormente a partir do banco de dados SQLite.
Agora, na função cellForRowAtIndexPath, você terá que mudar para que fique parecido com
o código seguinte:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell
SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
Animal *animal = (Animal *)[appDelegate.animals objectAtIndex:indexPath.row];
[cell setText:animal.name];
return cell;
}
Nós basicamente apenas
adicionamos três linhas abaixo da linha “// Set up the cell”, sendo a
primeira delas a mesma que adicionamos anteriormente para acessar o “application
delegate”.
A segunda linha cria um novo
objeto Animal baseado no array
do “application delegate”, e ele será usado para criar uma linha para cada
registro no banco de dados. Na linha final, apenas configuramos o texto da célula para
o campo ‘nome’ do objeto Animal.
Agora você pode executar o
programa, e você deveria visualizar uma tabela com os quatro animais que adicionamos ao banco de dados. Se você adicionou mais
animais que eu, você também deve ser capaz de visualizá-los aqui.
Agora vamos configurar o AnimalViewController. Abra o arquivo “AnimalViewController.h” e edite seu conteúdo desta maneira:
#import <UIKit/UIKit.h>
@interface AnimalViewController : UIViewController {
IBOutlet UITextView *animalDesciption;
IBOutlet UIImageView *animalImage;
}
@property (nonatomic, retain) IBOutlet UITextView *animalDesciption;
@property (nonatomic, retain) IBOutlet UIImageView *animalImage;
@end
O que fizemos acima foi adicionar
uma saída para a descrição e a imagem do Animal, e usaremos isso mais
tarde, quando fizermos o link com a visualização.
Agora abra o arquivo “AnimalViewController.m” e
adicione um chamado synthesize para a descrição e a imagem –
isso irá abaixo da linha “@implementation AnimalViewController”:
#import "AnimalViewController.h"
@implementation AnimalViewController
@synthesize animalDesciption, animalImage;
Agora é hora de fazer a
visualização detalhada da página aparecer quando você selecionar um registro. Abra
o arquivo “AnimalViewController.xib” dentro da pasta “resources” e o construtor
de interface deve aparecer.
A primeira coisa que devemos
fazer é configurar a classe File’s Owner para AnimalViewController,
e isso é feito ao selecionar o item “File’s Owner” na janela
principal, clicando em Tools > Identity Inspector no menu de cima,
e então selecionando AnimalViewController no menu suspenso da
classe.
Sua janela “inspector” deve estar
assim:
Agora vamos usar o UITextView para
a descrição (pois ele irá permitir a quebra de texto e a rolagem , no caso
de a descrição ser bastante grande) e o UIImageView para exibir a
imagem. O meu está assim:
Agora que já temos tudo feito, é
hora de ‘linkar’ tudo. Comece segurando control e clique+arraste
do “File’s Owner” para os objetos “View”. Um
pequeno menu cinza irá aparecer e você precisará selecionar view. Agora
segure control e clique+arraste do “File’s
Owner” para UITextView na janela do layout, e você deve
ver “animalDescription” na lista popup; selecione-o. Repita
esse processo para o UIImageView e você deve ver animalImage aparecer. Selecione-o também.
Agora salve a interface e feche o
construtor de interface.
Quase pronto! Tudo que temos que
fazer agora é configurar o código para quando um usuário selecionar um registro
na visualização da tabela.
Abra o arquivo “RootViewController.h” e
edite seu conteúdo da seguinte maneira:
#import <UIKit/UIKit.h>
#import "AnimalViewController.h"
@interface RootViewController : UITableViewController {
AnimalViewController *animalView;
}
@property(nonatomic, retain) AnimalViewController *animalView;
@end
Estamos criando uma instância do AnimalViewController para
ser usada pelo RootViewController quando o usuário selecionar um
item.
Agora abra o arquivo “RootViewController.m” e
edite a parte de cima do arquivo da seguinte maneira:
#import "RootViewController.h"
#import "SQLiteTutorialAppDelegate.h"
#import "Animal.h"
@implementation RootViewController
@synthesize animalView;
Isso irá apenas sintetizar o animalView que
acabamos de adicionar.
Primeiramente, vamos configurar o título padrão
da nossa visualização e, para fazer isso, você precisa usar “uncomment’ na
função viewDidLoad, e editá-la assim:
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to add the Edit button to the navigation bar.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.title = @"My Zoo";
}
Nós
também precisamos editar a função didSelectRowAtIndexPath nesse arquivo:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic -- create and push a new view controller
SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
Animal *animal = (Animal *)[appDelegate.animals objectAtIndex:indexPath.row];
if(self.animalView == nil) {
AnimalViewController *viewController = [[AnimalViewController alloc] initWithNibName:@"AnimalViewController" bundle:nil];
self.animalView = viewController;
[viewController release];
}
// Setup the animation
[self.navigationController pushViewController:self.animalView animated:YES];
// Set the title of the view to the animal's name
self.animalView.title = [animal name];
// Set the description field to the animals description
[self.animalView.animalDesciption setText:[animal description]];
// Load the animals image into a NSData boject and then assign it to the UIImageView
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[animal imageURL]]];
UIImage *animalImage = [[UIImage alloc] initWithData:imageData cache:YES];
self.animalView.animalImage.image = animalImage;
}
O que estamos fazendo aqui é
checando se o objeto animalView já foi criado e, se não foi, crie-o.
As próximas linhas são usadas
para configurar a animação (deslizar da direita para a esquerda) e para
configurar os campos de dados para os animais selecionados.
Agora você deve estar pronto para
executar o aplicativo e o ver em toda sua glória.
Você deve ver suas janelas assim:
5. Arquivos do projeto
Aqui estão os arquivos para este
projeto: Download dos arquivos do projeto
?
Texto original disponível em http://dblog.com.au/iphone-development-tutorials/iphone-sdk-tutorial-reading-data-from-a-sqlite-database/