RSS를 읽어 오는 간단한 리더기를 만들어 보겠습니다. 맥에서는 NSXMLDocument란 편리한 클래스가 있지만 아이폰 SDK에는 포함되어 있지 않습니다. 그렇기 때문에 NSXMLParser를 사용해서 RSS xml을 읽어오는 간단한 샘플을 만들어 보겠습니다.

인터넷을 통해 데이터를 가져오는 부분은 이전  "NSURLConnection으로 웹페이지 내용 가져오기"란 포스팅을 참고 하시기 바랍니다. 여기서는 파싱하는 부분만 간단히 살펴보겠습니다.


1. NSXMLParser 생성
xml 데이터 파싱은 네트워크로 데이터 수신이 완료된 후 불려지는 connectionDidFinishLoading 메소드에서 아래와 같이 처리합니다.

NSXMLParser *parser = [[NSXMLParser alloc] initWithData:receiveData];

[parser setDelegate:self];
[parser parse];
[parser release];

NSXMLParser 오브젝트를 수신된 데이터가 저장된 NSData 타입의 receiveData를 인자로 초기화를 합니다. setDelegate 메소드로 현재 오브젝트를 NSXMLParser의 딜리케이트로 지정합니다. 지정된 오브젝트는 요소별로 파싱의 시작/종료와 파싱된 스트링을 받을 수 있는 메소드를 구현해야 합니다.

parse 메소드로 파싱이 시작됩니다. 파싱은 자동으로 처리되지 않으며, 각 단계별로 딜리게이트된 메소드를 구현하여 필요와 형식에 맞게 직접 처리해야 합니다.

2. Delegate 메소드 구현
NSXMLParser에는 많은 딜리게이트 메소드가 있지만 가장 중요하고 거의 반드시 구현해야될 메소드는 parser:didStartElement, parser:foundCharacters, parser:didEndElement입니다.

parser:didStartElement로 한 요소의 파싱이 시작됨을 알수 있습니다. parser:foundCharacters로 해당 문자열들이 넘어 옵니다. 토큰 단위로 넘어 오기 때문에 넘어 오는 문자열들을 계속 저장해야 합니다. parser:didEndElement가 실행되면 비로소 한 요소의 파싱이 끝난 것을 알 수 있습니다. 이 메소드에서 해당 요소에 따른 필요한 처리를 합니다.

1) 시작 메소드 구현
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    if ([elementName isEqualToString:@"item"])
        elementType = etItem;
   
    [xmlValue setString:@""];
}

두번째 인자인 elementName으로 해당요소의 이름이 전달됩니다. 세번째와 네번째 인자는 네임스페이스와 관련된 uri와 전체이름이 전달됩니다. 만약 해당 xml이 네임스페이스를 사용한다면 이전에 [parser setShouldProcessNamespaces:YES];로 네임스페이스를 처리하도록 설정해야 합니다. NSXMLParser의 shouldProcessANamespace의 기본값은 NO 입니다.

마지막 인자인 attributeDict에는 해당 요소의 속성들이 전달됩니다. 만약 <item lang="ko"> 와 같이 되어 있다면 attributeDict 딕셔너리에 key가 'lang', value가 'ko'로 저장되어 전달됩니다.

여기서는 다른 인자들은 무시하고 item이란 이름의 요소가 시작될때 부터 데이터들을 저장하도록 요소이름이 item인지만 확인합니다. 그리고 xmlValue에 새로운 데이터를 저장하기 위해 이전에 저장된 값들을 초기화합니다.

2) 데이터 저장 메소드 구현
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if (elementType == etItem) {
        [xmlValue appendString:string];
    }
}
토큰별로 넘어오는 문자열을 xmlValue에 저장합니다.

3) 종료 메소드 구현
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if (elementType != etItem)
        return;

    if ([elementName isEqualToString:@"title"]) {
        [currectItem setValue:[NSString stringWithString:xmlValue] forKey:elementName];
    } else if ([elementName isEqualToString:@"link"]) {
        [currectItem setValue:[NSString stringWithString:xmlValue] forKey:elementName];
    } else if ([elementName isEqualToString:@"description"]) {
        [currectItem setValue:[NSString stringWithString:xmlValue] forKey:elementName];
    } else if ([elementName isEqualToString:@"category"]) {
        [currectItem setValue:[NSString stringWithString:xmlValue] forKey:elementName];
    } else if ([elementName isEqualToString:@"pubDate"]) {
        [currectItem setValue:[NSString stringWithString:xmlValue] forKey:elementName];
    } else if ([elementName isEqualToString:@"item"]) {
        [xmlParseData addObject:[NSDictionary dictionaryWithDictionary:currectItem]];
    }
}

한 요소가 끝날때 호출됩니다. 여기서는 RSS의 title, link, description, category, pubData 항목들만 currentItem 딕셔너리에 저장합니다. 한 포스팅의 마지막 요소인 </item>일 경우에는 xmlParseData에 현재 딕셔너리를 추가합니다.

3. 테이블뷰 출력
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

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

- (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] autorelease];
    }
   
    NSDictionary *dict = [xmlParseData objectAtIndex:indexPath.row];
    [[cell textLabel] setText:[dict objectForKey:@"title"]];
   
    return cell;
}

여기서 테이블뷰는 아무 동작을 하지않으며 xmlParseData에 저장된 해당 title만 출력합니다. 빌드 후 실행하면 아래와 같이 해당 RSS의 제목이 출력되는 것을 확인할 수 있습니다.

간단한 RSS 리더기를 구현해 보았습니다. 전체 소스는 아래의 압축파일을 다운로드 받아 확인하실 수 있습니다. 정확하고 자세한 내용은 아이폰 개발자 센터에서 제공하는 Introduction to Event-Driven XML Programming Guide for Cocoa 문서 또는 SeismicXML 샘플코드를 확인하시기 바랍니다.

'iOS' 카테고리의 다른 글

아이폰 OS 4  (8) 2010.04.09
NSXMLParser로 RSS 읽어오기  (21) 2009.08.05
인터페이스빌더 Table View Cell 사용하기  (0) 2009.06.25
cocos2d 개발환경 설정  (24) 2009.04.13
iPhone SDK 3.0 beta 2  (4) 2009.04.05
UITableView의 메모리 누수 현상  (4) 2009.02.05