はりまや日記

なんか適当にいろいろ綴ったりなんかするところ

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]);

これでフィードの解析はできるようになりますた。
次はこれをパラレルで動かしてみよう