Ir para conteúdo
  • Cadastre-se
Gabriel Vincent

Tutorial sobre Persistência de Dados no iOS

Posts Recomendados

Olá a todos! Surgiu uma dúvida nesse tópico: http://macmagazine.c...board-do-xcode/ que é uma dúvida bem comum entre aqueles que estão começando a desenvolver para iPhone: como salvar dados para que possa usá-los mais tarde, mesmo que o app tenha sido terminado?

Existem algumas formas de se fazer isso. Nesse tutorial ensinarei como salvar seus dados lendo e escrevendo Plists. Pra quem não sabe, Plists são arquivos com a mesma estrutura de um XML que são usados justamente para salvarem informações. Ao criar um projeto no XCode você automaticamente gera um arquivo Info.plist, que guarda informações sobre seu projeto e que será usado no iPhone para definir, por exemplo, quais os recursos suportados pelo app, qual a versão mínima do iOS que o usuário deve ter instalada para poder baixar o app, etc.

Neste tutorial, ensinarei como ler e escrever Plists usando uma biblioteca que eu escrevi para facilitar meu trabalho ao fazer apps que necessitam de persistência de dados. Baixe a biblioteca neste link: http://www.mediafire...dgis7nu48zm8xnn e adicione os arquivos ao projeto que criaremos em seguida:

Crie um novo projeto no XCode e escolha o template Single View Application. Nomeie o projeto como Pokedex e selecione a opção para usarmos o storyboard. Agora que o projeto foi criado, adicione a ele aqueles arquivos que você baixou: GVPlistPersistence.m e GVPlistPersistence.h. Não será necessário nesse tutorial o uso de blankPlist.plist então não precisa adicionar ao projeto este arquivo.

Neste tutorial quero não só mostrar como salvar dados no iPhone como também quero ensinar como adicionar/deletar rows de uma TableView. Sendo assim, vamos preparar nossos PokedexViewController.h e PokedexViewController.m para receberem uma TableView e serem capazes de maipular seus dados:

Em PokedexViewController.h faça com que seu código fique igual a esse:


#import <UIKit/UIKit.h>
@interface PokedexViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {

}
@end
[/CODE]

E em [i]PokedexViewController.m[/i] substitua todo o código no arquivo por esse:

[CODE]
//
// PokedexViewController.m
// Pokedex
//
// Created by Gabriel Vincent on 11/02/12.
// Copyright (c) 2012 _A_Z. All rights reserved.
//
#import "PokedexViewController.h"
@implementation PokedexViewController
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

// Configure the cell...

return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
@end
[/CODE]

Vá ao arquivo MainStoryboard.storyboard e arraste do menu de objetos à direita uma TableView para dentro da View que encontra-se na tela principal, como mostra a imagem:

c1TkP.png

No menu da esquerda, selecione a TableView que acabamos de adicionar à View principal e clique com o botão direito sobre ela. Ligue [i]DataSource[/i] e[i] Delegate [/i]a [i]Pokedex View Controller,[/i] também no menu da esquerda, como mostra a imagem:

hQWll.png

Ainda em MainStoryboard.storyboard, adicone também uma ToolBar ao topo da View principal, com 2 botões e um FlexibleSpace, como mostra a figura:

8Wdw0.png

Agora retorne a [i]PokedexViewController.h [/i]crie os seguintes objetos e métodos:

[CODE]
#import <UIKit/UIKit.h>
#import "GVPlistPersistence.h"

@interface PokedexViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *TableView;
IBOutlet UIBarButtonItem *editButton;
IBOutlet UIBarButtonItem *addButton;

NSMutableArray *pokemons;
GVPlistPersistence *plistManager;
}
- (IBAction)edit:(id)sender;
- (IBAction)add:(id)sender;
@end
[/CODE]

Fique atento para não esquecer de importar a biblioteca que nos ajudará a mexer com as Plists.

Volte ao MainStoryboard.storyboard e ligue todos os objetos que criamos aos seus devidos lugares: [i]TableView [/i] com Table View, [i]editButton [/i] com o botão Editar e [i]addButton [/i]com o botão com um sinal de +, como mostra a imagem:

YS4Zg.png

Abra [i]PokedexViewController.m [/i]e, em [i]ViewDidLoad [/i] adicione o código:

[CODE]
- (void)viewDidLoad
{
[super viewDidLoad];

plistManager = [[GVPlistPersistence alloc] init];
}
[/CODE]

É com esse objeto ([i]plistManager[/i]) que faremos toda a mágica de salvar os dados.

Vamos agora criar um novo ViewController, que possibilitará que adicionemos rows à TableView:

Clique com o botão direito sobre [i]PokedexViewController.m [/i] e selecione a opção [i]New File... [/i]Escolha um [i] UIViewController subclass [/i]e na tela seguinte nomeie a classe como [i]AddViewController[/i] e será subclass de UIViewController. Pode desmarcar a caixa coma upção de adicionar um .xib, pois já estamos usando Storyboard, como mostra a imagem:

Fk7m5.png

Volte ao MainStoryboard e crie outra ViewController, ao lado do que já existe:

bvphn.png

No menu da esquerda, selecione o ViewController que acabamos de criar e no menu da direita selecione o 3º ícone da esquerda para a direita. No campo de texto [i]Class [/i]apague o que estiver escrito e escreva [i]AddViewController[/i], conforme a imagem:

vaOAl.png

Vamos aproveitar as facilidades que o Storyboard tem a nos oferecer: selecione, na View principal, no botão com sinal de + e em seguida clique com o botão direito sobre ele. Selecione [i]Modal[/i] e ligue-o ao ViewController que criamos há pouco, como mostra a imagem:

NCwOn.png

Ao [i]AddViewController [/i]adicione uma ToolBar ao topo da View da mesma forma como fizemos com o outro ViewController, com os mesmos 2 botões, separados por um Flexible Space. Mas dessa vez o botão da esquerda se chamará [i]Salvar [/i] e o da direita [i]Cancelar[/i].

Logo abaixo da ToolBar, adicione uma Label e um TextField, como mostra a imagem:

sJmda.png

A questão do design é irrelevante. Escolha a cor de fundo que você preferir ou deixe em branco mesmo.

Agora vá ao [i]AddViewController.h [/i]e crie os seguintes objetos e métodos:

[CODE]
@interface AddViewController : UIViewController {
IBOutlet UITextField *textField;
}
- (IBAction)cancel:(id)sender;
- (IBAction)save:(id)sender;
@end
[/CODE]

Retorne ao MainStoryBoard e ligue o recém criado objeto textField ao recém criado TextField na View. Ligue também os métodos que criamos aos botões [i]Cancelar [/i]e [i]Salvar[/i]:

26YAU.png

Vá a [i]AddViewController.m [/i] e implemente os métodos criados anteriormente:

[CODE]
- (IBAction)save:(id)sender {

}
- (IBAction)cancel:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
[/CODE]

O conteúdo do método [i]save[/i] será escrito em breve, mas antes salve e execute o código para você ver o que fizemos até agora:

http://i.imgur.com/eG4uB.png (OBS.: A partir desse ponto as imagens só serão vistas se você clicar no link, porque o fórum não permite que eu poste tantas imagens.)

Como você viu, temos agora 2 View Controller: o principal com a TableView e o que aparece quando clicamos em +, com um TextField, que será onde adicionaremos os nomes dos Pokémons. Ao clicar em [i]cancelar [/i]esse segundo ViewController se esconde e voltamos ao primeiro ViewController.

Vamos à manipulação dos dados agora!

Volte ao [i]AddViewController.h [/i] e importe, como já fizemos antes, a class GVPlistPersistence.h. Crie também aquele objeto que já criamos no ViewController principal, que será usado para fazer a ponte com as Plists:

[CODE]
#import <UIKit/UIKit.h>
#import "GVPlistPersistence.h"
@interface AddViewController : UIViewController {
IBOutlet UITextField *textField;

GVPlistPersistence *plistManager;
}
- (IBAction)cancel:(id)sender;
- (IBAction)save:(id)sender;
@end
[/CODE]

Agora em [i]AddViewController.m[/i] façamos a mágica:

[CODE]
- (IBAction)save:(id)sender {

NSMutableArray *verificationArray = [plistManager plistFromDocumentsFolderNamed:@"Pokemons.plist"];
if (verificationArray.count == 0) [plistManager createPlistFileWithName:@"Pokemons.plist"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObject:textField.text] forKeys:[NSArray arrayWithObject:@"Pokemon"]];
[plistManager addSection:dict ToPListWithFileName:@"Pokemons.plist"];

[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)cancel:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidLoad
{
[super viewDidLoad];

plistManager = [[GVPlistPersistence alloc] init];

[self dismissModalViewControllerAnimated:YES];

}
[/CODE]

Mais uma vez, salve e execute seu código. Clique no botão de + e escreva o nome de um Pokémon no TextField e clique em [i] Salvar[/i]. A View vai se esconder e nada vai acontecer. À primeira vista. Na verdade, você já salvou as informações na Plist, mas como ainda não programamos nada para [i]mostrar[/i] essas informações, você não viu nada de diferente. Mas uma Plist chamada [i]Pokemons.plist[/i] foi criada e nela foi inserido o nome do Pokémon que você digitou. Para ver isso de perto navegue pelo Finder pelo caminho:

Users/seu-usuario/Library/Application Support/iPhone Simulator/numero da versao do iOS simulator que você está usando (uso a 5.0)/Applications/numeros estranhos (vá entrando nas pastas até achar a que contém um [i]Pokedex.app[/i]/Documents

Dentro dessa pasta averá um arquivo chamado [i]Pokemons.plist. [/i]Selecione-o e aperte a barra de espaço para abrir com o Quick Look. Você verá que o Pokémon que você adicionou estará lá, salvo. Adicione outros para vê-los sendo adicionados à Plist.

http://i.imgur.com/AvGd7.png

Cada <dict></dict> corresponde a um Pokémon adicionado. Key é como se fosse o nome da variável e que está entre <string></string> é o valor dessa variável. Você pode, em um <dict></dict>, criar quantas Keys e Values para essas Keys você quiser. Por exemplo: você poderia criar algo como:

[CODE]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>Pokemon</key>
<string>Dragonite</string>
<key>Attack</key>
<string>Hiperbeam</string>
<key>Type</key>
<string>Dragon</string>
</dict>
<dict>
<key>Pokemon</key>
<string>Charizard</string>
<key>Attack</key>
<string>Flamethrower</string>
<key>Type</key>
<string>Fire</string>
</dict>
<dict>
<key>Pokemon</key>
<string>Haunter</string>
<key>Attack</key>
<string>Night Shade</string>
<key>Type</key>
<string>Ghost</string>
</dict>
</array>
</plist>
[/CODE]

Vamos agora mostrar esses Pokémons em nossa TableView:

Volte ao [i]PokedexViewController.m [/i]e à [i]ViewWillAppear[/i] adicione o código:

[CODE]
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];

pokemons = [plistManager plistFromDocumentsFolderNamed:@"Pokemons.plist"];
[TableView reloadData];
}
[/CODE]

E aos métodos relacionados à TableView adicione:

[CODE]
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [pokemons count]; //Terá tantas rows quanto forem os objetos dentro da Array
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

// Configure the cell...

cell.textLabel.text = [[pokemons objectAtIndex:indexPath.row] objectForKey:@"Pokemon"];

return cell;
}
[/CODE]

O que fazemos toda vez que o View vai aparecer, ou seja, quando o app inicia e quando [i]AddViewController[/i] some após um novo Pokémon ter sido adicionado é ler o arquivo Plist que foi escrito ao salvar o novo Pokémon e recarregar a TableView, para que o método [color=#000000]- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath [font=arial,helvetica,sans-serif][size=4]seja chamado e popule as rows com o objeto correspondente à Key "Pokemon" em cada <dict></dict> da Plist. É exatamente o que faz o techo do código:[/size][/font][/color]

[color=#000000]cell.textLabel.text = [[pokemons objectAtIndex:indexPath.row] objectForKey:@"Pokemon"];[/color]

[font=arial, helvetica, sans-serif][color=#000000]Salve e execute o código. Clique no botão + e Salve um novo Pokémon. Quando você clicar em [i]Salvar[/i] verá que esse novo pokémon foi adicionado à TableView. Uhul, fácil, não? Você verá que mesmo fechando o app e depois abrindo-o os Pokémons anteriormente adicionados continuam lá. [/color][/font]

[font=arial, helvetica, sans-serif][color=#000000]Agora vamos aprender a deletar um Pokémon de nossa Pokédex:[/color][/font]

Volte ao Storyboard e clique com o botão direito no menu da esqueda sobre [i]Pokedex View Controller [/i]e ligue o método [i] edit[/i] ao botão editar:

http://i.imgur.com/meKpa.png

[font=arial, helvetica, sans-serif][color=#000000]Em [i]PokedexViewController.m[/i] adicione esse método:[/color][/font]

[font=arial, helvetica, sans-serif][color=#000000]

[CODE]
- (IBAction)edit:(id)sender {

if (TableView.editing == NO) {
[TableView setEditing:YES animated:YES];
editButton.title = @"OK";
editButton.style = UIBarButtonItemStyleDone;
}
else {
[TableView setEditing:NO animated:YES];
editButton.title = @"Editar";
editButton.style = UIBarButtonItemStyleBordered;
}

}
[/CODE]

[/color][/font]

[font=arial, helvetica, sans-serif][color=#000000]Esse método será chamado quando o botão [i]Editar[/i] for apertado. Ele verificará se a TableView está em modo de edição. Se não estiver, entra em modo de edição e muda o título do botão [i]Editar[/i] para "OK". Caso esteja sim em modo de edição, sai do modo de edição e altera o título de "OK" de volta para "Editar". Note que se você arrastar o dedo em uma row da direita pra esquerda ela entra sozinha em modo de edição[/color][/font]

[font=arial, helvetica, sans-serif][color=#000000]Agora adicione esse outro método que será responsável por deletar da Plist a <dict></dict> correspondente ao Pokémon que você quer deletar:[/color][/font]

[font=arial, helvetica, sans-serif][color=#000000]

[CODE]
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[pokemons removeObjectAtIndex:indexPath.row];
[TableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationMiddle];

[plistManager saveArray:pokemons ToPlistWithFileName:@"Pokemons.plist"];
}
}
[/CODE]

[/color][/font]

[font=arial, helvetica, sans-serif][color=#000000]Esse método primeiramente remove da Array [i]pokemons[/i] o objeto com index igual ao da row selecionada para ser deletada. Depois deleta essa mesma row na TableView com a animação [i]UITableViewRowAnimationMiddle. [/i]Por fim, deleta da Plist o <dict></dict> correspondente ao Pokémon deletado, para que, quando o app for fechado e depois reaberto, esse Pokémon deletado não volte a aparecer na TableView.[/color][/font]

Bom, pessoal, é isso. Espero que quem não sabia como fazer persistência de dados no iOS tenha entendido. Há ainda muito a ser explorado com relação a persistência de dados. Você pode salvar configurações, pode salvar diferentes Keys à Plist, pode fazer uma infinidade de coisas. Tente fazer coisas mais complexas a partir desse tutorial, pesquise, descubra novas possibilidades!

Esse foi o primeiro tutorial que fiz, então não sei se ficou claro o suficiente nem se ficou bem feito. Dêem um feedback pra eu saber se entenderam e poder fazer melhores no futuro. Qualquer dúvida, sou todo ouvidos!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Parabéns Grabriel.

Tem noção de como fazer a mesma coisa usando um banco de dados mesmo, de preferencia o Core Data?????

Abraço e vlw pela ajuda..

Compartilhar este post


Link para o post
Compartilhar em outros sites

Parabéns Grabriel.

Tem noção de como fazer a mesma coisa usando um banco de dados mesmo, de preferencia o Core Data?????

Abraço e vlw pela ajuda..

EU adoraria te ajudar com isso, mas infelizmente não sei Core Data. Tá na minha lista de coisas para aprender esse ano.

Compartilhar este post


Link para o post
Compartilhar em outros sites

EU adoraria te ajudar com isso, mas infelizmente não sei Core Data. Tá na minha lista de coisas para aprender esse ano.

Ok!!!! Valer assim mesmo. Estou estudando ele e qualquer novidade te passo!!!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Surgiu um problema estou fazendo um tipo de historico e presciso de uma funcao de "search":

SearchView.jpg

Alguma solução?

Já agradeço. ;)

Isso envolve alguns outros métodos e toda uma lógica. Pra começar, vou te dizer que você precisa de 2 arrays: uma com todos os itens e outra que vai receber os resultados das buscas. A esse textField da searchBar, conecte uma IBAction que, a cada modificação de conteúdo, chama um método que verifica se tem algo igual ao que foi digitado na array que está com todo o conteúdo. Cada coisa que tiver, coloca na 2ª array, e mostra na TableView. Tenho um projeto com isso pronto, mas como tinha algumas outras coisas a fazer com o código, está um pouco mais complexo do que apenas simples buscas. Quer o projeto?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pronto, esse aqui tá mais fácil de entender: Pokedex.zip

Note que:

- No MainStoryBoard você deverá adicionar um SearchBar and Search Display Controller, e não uma SearchBar.

- A classe PokedexViewController.h deverá ser delegate de UISearchBar

- O método que executa a busca tem um if que eu fiz de modo a realizar buscas Case Insensitive, ou seja, que não diferenciam letras maiúsculas de minúsculas. Se você quiser que sua busca seja Case Sensitive, delete, dentro do if, tudo desde ||, que é o controlador ou.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É possível criar uma funao que nao deixe dois itens com o mesmo nome?

Ligue ao botão Salvar, em AddPokemonViewController.m, um método que enche uma array com a plist com a lista de Pokémons e pesquisa nessa array se já tem algum objeto com o mesmo nome que o usuário está tentando adicionar. Caso haja, mostre um mensagem de erro, caso não haja, salva normalmente o novo ítem na lista.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Claro. Use um TextField pra pegar o nome do que você quer que seja excluído. Enche uma array com o conteúdo da plist com os itens da TableView. Faz um for que vai verificando de um em um os objetos da array, pra ver qual tem o título igual ao digitado pelo usuário. Quando tiver, deleta o objeto naquele índex da array, a row da TableView com aquele index, dá um break (porque não haverão dois objetos com o mesmo título mesmo) e reload na TableView.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Visitante
Responder este tópico…

×   Você colou conteúdo com formatação.   Remover formatação

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Limpar editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Quem Está Navegando   0 membros estão online

    Nenhum usuário registrado visualizando esta página.



  • Estatísticas do Fórum

    • Total de Tópicos
      48.574
    • Total de Posts
      415.363
×
×
  • Criar Novo...