Home » Tutoriais » Programação » Como montar um simples leitor de RSS para o iPhone

Como montar um simples leitor de RSS para o iPhone

:: por Redação macmais :: 05/07/2011 :: 2 comentários

Por Lucas Longo*

Vamos continuar com o desenvolvimento do aplicativo para o site da MAC+. Agora, chegou o momento de montar o leitor de RSS (Really Simple Syndication), que vai mostrar as últimas notícias do site. Os passos para isso são simples: primeiro, temos de pegar o endereço (URL) do XML do RSS e analisá-lo; depois, é preciso montar o leitor deste XML no iPhone usando o NSXMLParser; na sequência, aprenderemos como baixar o XML em si de forma assíncronica, para que o usuário não precise ficar travado em alguma parte da interface aguardando o download do XML; finalizando, será necessário montar uma tabela para exibir os dados e, ao clicar em uma das notícias, abrir a pagina da matéria dentro do próprio aplicativo. Prontos?

Mais notícias

Para começar, precisamos baixar o XML do RSS do site para análise. No caso da MAC+, o endereço é http://macmais.com.br/category/noticias/feed.

Analisando o XML, vemos que temos uma tag inicial chamada <channel>, algumas informações sobre o RSS em si nas tags <title>, <link> etc. O que realmente no interessa é o que está na tag <item> – ali, temos o título do item, o link, data de publicação e categorias.

O que queremos extrair, portanto, são os conteúdos dentro das tags <title>, <link> e <pub date> <channel>
<title>Mac+ &#187; Notícias</title>
<atom:link href=”http://macmais.com.br/category/noticias/feed/” rel=”self” type=”application/rss+xml” />
<link>http://macmais.com.br</link>
<description></description>
<lastBuildDate>Fri, 04 Mar 2011 20:09:23 +0000</lastBuildDate>
<generator>http://wordpress.org/?v=2.9.1</generator>
<language>en</language>
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<item>
<title>“Apple” é marca registrada em 42 classes internacionais</title>
<link>http://macmais.com.br/noticias/%e2%80%9capple%e2%80%9d-e-marca-registrada-em-42-classes-internacionais/</link>
<comments>http://macmais.com.br/noticias/%e2%80%9capple%e2%80%9d-e-marca-registrada-em-42-classes-internacionais/#comments</comments>
<pubDate>Fri, 04 Mar 2011 20:04:33 +0000</pubDate>
<dc:creator>Bianca Hayashi</dc:creator>
<category><![CDATA[Apple]]></category>
<category><![CDATA[Notícias]]></category>
<category><![CDATA[educação]]></category>
<category><![CDATA[Games]]></category>
<category><![CDATA[jogos]]></category>
<category><![CDATA[marca registrada]]></category>
<category><![CDATA[Medicina]]></category>
<category><![CDATA[patente]]></category>
<category><![CDATA[registro]]></category>
<category><![CDATA[serviços legais]]></category>
<category><![CDATA[telecomunicações]]></category>
<guid isPermaLink=”false”>http://macmais.com.br/?p=20495</guid>
<description><![CDATA[Sim, a Apple quer dominar o mundo]]></description>
<wfw:commentRss>http://macmais.com.br/noticias/%e2%80%9capple%e2%80%9d-e-marca-registrada-em-42-classes-internacionais/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>

Será necessário fazer o parsing do XML. Abrimos o RSSMainViewController.m e removemos os comentários do View did load para criar o parser. Veja como:

- (void)viewDidLoad {
[super viewDidLoad];
noticiasArray = [[NSMutableArray alloc] init];
NSURL* url = [NSURL URLWithString:@”http://macmais.com.br/category/noticias/feed/“];
NSXMLParser* parser = [[NSXMLParser alloc]
initWithContentsOfURL:url];
[parser setDelegate:self];
[parser parse];
[parser release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.

// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

Perceba que ao iniciar o parser, já estamos fazendo o download do XML sincronicamente. Mais adiante, veremos como realizar esse processo de modo assíncrônico.

A segunda coisa a reparar é a mudança do método setDelegate para self. Self é a nossa classe, o RSSMainViewController. O Delegate é um conceito que diz para um objeto quem vai responder às mensagens geradas por ele. Por exemplo: vamos dizer que o objeto na nossa classe é um “cachorro”. Ele tem um dono, o Delegate, no caso, você. Esse “cachorro” alguma hora vai querer sair de casa para fazer suas necessidades. Mas ele não sabe abrir uma porta, só sabe latir. Esse “latido” está definido no seu protocolo de comunicação do “cachorro” com seu dono (Delegate). Portanto, quando ele late, vai procurar no seu Delegate a função de abrir a porta. Você, então, ouve o latido e abre a porta. É uma maneira de objetos se comunicarem entre si de uma maneira estruturada. Então, nosso “cachorro”, o NSXMLParser “late” os seguintes métodos, além de vários outros:

-(void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict;

O método acima é chamado toda vez que o parser encontra uma tag (element-Name). Também recebemos um NSDictionary com os elementos dentro de uma tag do XML, como <item nome=’lucas’ sobrenome=’longo’>. Um dicionário é um vetor de objetos indexados por uma chave, geralmente uma string, em vez de um índice como são os vetores. São muito úteis para passar e acessar informações de um objeto usando linguagem “normal”.

Portanto, no caso do nosso exemplo, podemos acessar o valor da chave “nome” simplesmente pedindo ao dicionário o objeto para a chave “nome”.

-(void) parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string;

O método acima é chamado sempre que o parser encontra um caractere entre duas tags – isso inclui a quebra de linha, é bom lembrar!

-(void) parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName;

O método acima é chamado sempre que o parser encontrar o fechamento de uma tag (elementName).

Pode ser um pouco confuso no início, mas a grande vantagem é que o parser já faz o loop pelo XML automaticamente – e podemos pará-lo quando quisermos. Agora precisamos de variáveis para armazenar os valores que encontramos enquanto percorremos o XML. Vamos ter um vetor (NSArray) de dicionários (NSDictionary). Durante o loop do XML, armazenaremos a tag sendo lida (currentElement) e também armazenar o valor entre elas. Em nosso header, declaramos essas variáveis e indicamos que esta classe vai implementar o protocolo do delegate do parser, o NSXMLParserDelegate.

@interface RSSMainViewController : UITableViewController
<NSXMLParserDelegate> {
NSMutableArray* noticiasArray;
NSMutableDictionary* noticiaDict;
NSMutableString* elementString;
NSString* currentElement;
}
@end

Aqui, estamos usando as versões “mutable” do NSArray e NSDictionary para poder alterá-las durante a execução do nosso código.

No view did load, inicializamos nosso array de notícias.

- (void)viewDidLoad {
[super viewDidLoad];
noticiasArray = [[NSMutableArray
alloc] init];

Agora no loop do parser, queremos inicializar um dicionário para armazenar as informações dentro da tag <item>.

Então, quando o elementName for igual a “item”, criamos o dicionário. Quando o parser terminar de ler a tag <item>, vamos “destruir” o dicionário.
-(void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
if ([elementName
isEqualToString:@”item”])
noticiaDict = [[NSMutableDictionary alloc] init];

}
}

Também precisamos armazenar o elementName para saber em que ponto do XML o parser está. Vamos pegar o elementName somente quando houver um dicionário criado. Neste momento, inicializamos o elementName e o elementString, em que vamos armazenar o conteúdo entre as tags. Nosso método fica assim:

-(void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {

if (noticiaDict) {
currentElement = [[NSString
alloc] initWithString:elementName];
elementString = [[NSMutableString
alloc] init];
}

if ([elementName
isEqualToString:@”item”]) {

noticiaDict = [[NSMutableDictionary
alloc] init];
}
}

Agora, quando encontramos o fechamento de uma tag, montaremos nossa estrutura. Se encontramos a tag <item> fechando, isso quer dizer que já passamos por todas as suas subtags e temos um dicionário da notícia pronto. Colocamo-lo em nosso vetor. Também vamos “destruir” nossas variáveis para não ter vazamento de memória (release) e para poder checar se ela existe ou não com facilidade (nil).

-(void) parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {

if ([elementName
isEqualToString:@”item”]) {
[noticiasArray
addObject:noticiaDict];
[elementString release];

elementString = nil;
[currentElement release];
currentElement = nil;
[noticiaDict release];
noticiaDict = nil;
}
}

No caso de a tag ser um dos intermediários, temos de adicionar a string ao dicionário, somente quando existir um dicionário, o currentElement e o elementString.

Nosso código fica assim:
-(void) parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {

if (noticiaDict && currentElement
&& elementString) {
[noticia-
Dict setObject:elementString
forKey:currentElement];
[currentElement release];
currentElement = nil;
[elementString release];
elementString = nil;
}
if ([elementName
isEqualToString:@”item”]) {
[noticiasArray
addObject:noticiaDict];
[elementString release];
elementString = nil;
[currentElement release];
currentElement = nil;
[noticiaDict release];
noticiaDict = nil;
}
}

Finalmente, precisamos concatenar a string quando encontrar um caractere, mas somente quando tivermos um current element.

-(void) parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string {
if (currentElement) {
[elementString
appendString:string];
}
}

A seguir, vamos remontar a tabela
quando terminarmos o parsing. Observe
que estamos chamando o self.tableView,
pois nossa classe é um table view controller
e tem como propriedade um table view.
-(void) parserDidEndDocument:(NSXMLPars
er *)parser {
[self.tableView reloadData];
}
No método Data source e Delegate do Table View, provemos as informações para a tabela. O conceito do data source é o mesmo do delegate mas ele pede informações para que o objeto possa ser criado corretamente. Aqui, observamos o método que ele chama quando está montando as linhas de uma seção da tabela. Retornamos quantos itens temos em nosso array.

- (NSInteger)tableView:(UITableView *)
tableView numberOfRowsInSection:(NSIntege
r)section {
return [noticiasArray count];
}

No próximo método, fornecemos uma célula para uma das linhas da tabela. A célula já tem um label para utilizar, o textLabel.

Então, puxamos do nosso vetor o dicionário da notícia. Do dicionário pegamos o objeto da chave título e a colocamos no label da célula (abaixo do //Configure with cell…).

Todo este código que já vem escrito no modelo da Apple serve para reutilizar as células – o table view basicamente mantém em memória somente as células visíveis e, portanto, implementa o esquema de reutilizar células.

- (UITableViewCell *)
tableView:(UITableView *)tableView cellForR
owAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier =
@”Cell”;
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIde
ntifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefau
lt reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell…
NSMutableDictionary* dict = [noticiasArray
objectAtIndex:indexPath.row];
cell.textLabel.text = [dict
objectForKey:@”title”];
return cell;
}

E voilá!

Lucas Longo (@iaibrasil) é diretor do IAI, Instituto de Aprendizagem Interativa, e adora seu iPhone.

*Matéria publicada originalmente na MAC+ 60.

2 comentários

  1. Claudio comentou 0:16 às 16 de setembro de 2011

    Olá Lucas, Muito bom seu tutorial. Simples e bem explicado. Vi diversos na internet, mas sempre o cara quer matar uma formiga com um canhão. Lógica, quanto mais simples melhor. No caso estou fazendo um rss com imagem na cell e li algo a respeito de que teria que ser feito um carregamento prévio para que não afetasse o carregamento do feed. Procede isso.

Deixe um comentário

 

Publicidade