iphoneアプリ開発日記その6
RSSリーダーを作ろう その2
フィードデータを取得する
さて、xml 解析が出来るようになったので、RSS フィードのデータを取得しませう
前回見たように、必要な情報はこんな感じ
- channel か item か
- title
- link
- description
- pubDate
description 要らないような気がしますが、まあそのままいきましょう。
それで、これらの情報を格納する箱を定義します。
一番真っ当なやり方は、データクラスを作ってそれに突っ込むことでしょう。
しかし、今回は対して要素も多くないので、Dictionary に持たせてみましょう。
データ構造はこんな感じ
(NSDictionary) + channnel (NSDictionary) + title + link + description + date + itemData (NSArray) + item (NSDictionary) + title + link + description + date + item...
で、これを処理するコードがこんなん
@interface RSSParser : NSObject <NSXMLParserDelegate>{ NSXMLParser* parser; NSMutableDictionary* feedsDict; BOOL isChannel; BOOL isItem; BOOL isTitle; BOOL isLink; BOOL isDescription; BOOL isDate; }
@implementation RSSParser - (NSMutableDictionary*)parseRSS: (NSURL*)url{ parser = [NSXMLParser.new initWithContentsOfURL:url]; parser.delegate = self; [parser parse]; return feedsDict; } - (void)parserDidStartDocument:(NSXMLParser *)parser{ feedsDict = NSMutableDictionary.new; [feedsDict setObject:NSMutableDictionary.new forKey:FeedConstant.getChannelKey]; [feedsDict setObject:NSMutableArray.new forKey:FeedConstant.getItemKey]; isChannel = NO; isItem = NO; isTitle = NO; isLink = NO; isDescription = NO; isDate = NO; } - (void)parserDidEndDocument:(NSXMLParser *)parser{ isChannel = NO; isItem = NO; isTitle = NO; isLink = NO; isDescription = NO; isDate = NO; } - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ if ([elementName isEqualToString:FeedConstant.getChannelKey]){ isChannel = YES; }else if ([elementName isEqualToString:FeedConstant.getItemKey]){ isItem = YES; [[feedsDict objectForKey:FeedConstant.getItemKey] addObject:NSMutableDictionary.new]; }else if ([elementName isEqualToString:FeedConstant.getTitleKey]){ isTitle = YES; }else if ([elementName isEqualToString:FeedConstant.getLinkKey]){ isLink = YES; }else if ([elementName isEqualToString:FeedConstant.getDescriptionKey]){ isDescription = YES; }else if ([elementName isEqualToString:FeedConstant.getPubDateKey] || [elementName isEqualToString:FeedConstant.getDCDateKey]){ isDate = YES; } } - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ if ([elementName isEqualToString:FeedConstant.getChannelKey]){ isChannel = NO; }else if ([elementName isEqualToString:FeedConstant.getItemKey]){ isItem = NO; }else if ([elementName isEqualToString:FeedConstant.getTitleKey]){ isTitle = NO; }else if ([elementName isEqualToString:FeedConstant.getLinkKey]){ isLink = NO; }else if ([elementName isEqualToString:FeedConstant.getDescriptionKey]){ isDescription = NO; }else if ([elementName isEqualToString:FeedConstant.getPubDateKey] || [elementName isEqualToString:FeedConstant.getDCDateKey]){ isDate = NO; } } - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ NSString* trimmedString = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (![trimmedString isEqualToString:@""]) { if (isChannel) { if (isTitle) { [[feedsDict objectForKey:FeedConstant.getChannelKey] setObject:string forKey:FeedConstant.getTitleKey]; } else if (isLink) { [[feedsDict objectForKey:FeedConstant.getChannelKey] setObject:string forKey:FeedConstant.getLinkKey]; } else if (isDescription) { [[feedsDict objectForKey:FeedConstant.getChannelKey] setObject:string forKey:FeedConstant.getDescriptionKey]; }else if (isDate) { [[feedsDict objectForKey:FeedConstant.getChannelKey] setObject:string forKey:FeedConstant.getDateKey]; } }else if (isItem) { if (isTitle) { [[[feedsDict objectForKey:FeedConstant.getItemKey] lastObject] setObject:string forKey:FeedConstant.getTitleKey]; } else if (isLink) { [[[feedsDict objectForKey:FeedConstant.getItemKey] lastObject] setObject:string forKey:FeedConstant.getLinkKey]; } else if (isDescription) { [[[feedsDict objectForKey:FeedConstant.getItemKey] lastObject] setObject:string forKey:FeedConstant.getDescriptionKey]; } else if (isDate) { [[[feedsDict objectForKey:FeedConstant.getItemKey] lastObject] setObject:string forKey:FeedConstant.getDateKey]; } } } } - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{ NSLog(@"error: %@", parseError); } @end
これで必要なデータがとれました。
カテゴリを使ってみる
とりあえずフィード情報が使えるようになったのだが、Dictionary で下層までアクセスするのがめんどくさい。
なので、カテゴリーの練習がてらにNSDictionary を拡張することを考えた
カテゴリーとは、動的にメソッド追加できる機能のこと
Objective-C - Wikipedia
いろいろ制約はあるが、クラス定義を複数のファイルに分散できることになる。
また、wikiにもあるが基盤系(NS〜ってやつ)に簡単に機能追加できる。
これで NSDictionary にアクセサを追加してみた
こんな感じ。定数は適当に定義してください。
NSDictionary+FeedAccessor.h #import <Foundation/Foundation.h> @interface NSDictionary (FeedAccessor) - (id)getChannelData; - (id)getItemData; - (NSString*)getTitle; - (NSString*)getLink; - (NSString*)getDescription; - (NSString*)getDate; @end NSDictionary+FeedAccessor.m #import "NSDictionary+FeedAccessor.h" #import "FeedConstant.h" @implementation NSDictionary (FeedAccessor) - (id)getChannelData{ return [self objectForKey:FeedConstant.getChannelKey]; } - (id)getItemData{ return [self objectForKey:FeedConstant.getItemKey]; } - (NSString *)getTitle{ return [self objectForKey:FeedConstant.getTitleKey]; } - (NSString *)getLink{ return [self objectForKey:FeedConstant.getLinkKey]; } - (NSString *)getDescription{ return [self objectForKey:FeedConstant.getDescriptionKey]; } - (NSString *)getDate{ return [self objectForKey:FeedConstant.getDateKey]; } @end
使うときはこう
RSSParser *parser = RSSParser.new; NSDictionary* feed = [parser parseRSS:[NSURL URLWithString:url]]; NSLog(@"%@", [feed getChannelData]getTitle]);
これでフィードの解析はできるようになりますた。
次はこれをパラレルで動かしてみよう