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
모든 댓글을 환영합니다. (욕설과 광고는 삭제합니다.)
  1. Favicon of http://icarusx.tistory.com BlogIcon ICARUSX 2009.08.21 13:29  댓글주소  수정/삭제  댓글쓰기

    오오~~ 드뎌... XML 관련 부분을 올려주셨군요 ㅎㅎ

    한동안 휴가다 뭐다 해서 정신없이 잠수를 타고있었네요 ^^
    슬슬 사이트만드는것도 잘되어가고있고..

    바로 만들어봐야겠습니다.. 우선 시간이 않되니.. 즐겨찾기를 쿨럭;;
    감사해요 ㅎ

    뒤늦은 무더위 조심하세요 ㅎㅎ

    P.s : 똑같은 코멘트를 다른곳에 잘못날렸는데... 비번을 까먹었어요 -_-;; 죄송;

    • Favicon of https://www.cocoadev.co.kr BlogIcon cocoadev 2009.08.24 15:43 신고  댓글주소  수정/삭제

      휴가 잘 갔다 오셨는지요?

      참고만 하시고 애플의 관련 문서와 샘플을 보시는 것이 좋을 것 같습니다. 잘못 달린 댓글을 제가 처리하겠습니다. :)

  2. Favicon of http://icarusx.tistory.com BlogIcon ICARUSX 2009.08.25 10:38  댓글주소  수정/삭제  댓글쓰기

    ^^;; 네 휴가는 잘다녀왓습지요... 벌써 가을로 접어든 모양이에요 ㅎㅎ;; 쌀쌀해지네요 -_-
    감기조심하시길 ㅎㅎㅎ

  3. grogg 2009.09.15 18:39  댓글주소  수정/삭제  댓글쓰기

    아이폰 관련 xml 검색하다가 찾아오게 되었습니다.
    좋은 정보 감사합니다.

  4. skyzzz 2010.03.10 00:03  댓글주소  수정/삭제  댓글쓰기

    첨부해주신 샘플코드를 다운로드 받아서 실행을 해보니 잘되네요.
    근데 의문점이 있어 질문드립니다 ...

    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if (elementType == etItem) {
    [xmlValue appendString:string];
    }
    }

    위 부분에서 elementType 과 etItem 변수?는 어디서 나온것인지요?
    답변 부탁드리겠습니다. (__);

  5. BlogIcon 연애사진 2010.03.28 14:01  댓글주소  수정/삭제  댓글쓰기

    안녕하세요, 공개해주신 글을 보고 열심히 따라하며 배우고 있습니다.

    그런데 한가지 여쭤보려고 합니다만 이 RSS의 경우 셀 제목 부분에 본문에 있는 이미지를 부분적으로나마 불러와서 비쥬얼하게 보여주려고 합니다.

    마치 뉴스 앱들의 기사 표현처럼 하려고 한다면 어떻게 해야 할까요?

    • Favicon of https://www.cocoadev.co.kr BlogIcon cocoadev 2010.04.09 15:58 신고  댓글주소  수정/삭제

      죄송합니다. 댓글을 이제서야 보았네요. UITableViewCell을 커스터마이징해서 사용해야 합니다. 애플 개발자 사이트에 보시면 관련 샘플들이 있습니다.

  6. enigma 2010.04.21 11:47  댓글주소  수정/삭제  댓글쓰기

    정말 감사합니다. 많은 도움 되었습니다.

    제가 cocoadev님 소스를 받아서 더보기버튼(Disclosure Button)을 만들어 UITextView에 <description>에 있는 내용을 나오게 했는데 문자는 잘나오는데 그림같은 경우는 소스그대로 나오더군요 이건 어떻게 처리해야 하는지 조그만 팁 좀 부탁드리면 안될까요? ㅜㅜ

  7. enigma 2010.04.27 11:05  댓글주소  수정/삭제  댓글쓰기

    제가 좀 게을러서 이제야 해결해서 글올립니다. UITextView 보다는 UIWebView에 파싱된 string을 읽는 메소드가 있더군요 그걸로 하니 훨씬 편하고 좋네요...ㅋㅋ
    cocoadev님 덕분에 해결했습니다. 정말 감사합니다...^^

  8. 2010.04.30 14:55  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  9. 2010.05.10 14:41  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • Favicon of https://www.cocoadev.co.kr BlogIcon cocoadev 2010.05.13 11:22 신고  댓글주소  수정/삭제

      글 하단에 전체 소스코드를 다운로드 받으실 수 있는 링크가 있습니다. 다운로드 받으셔서 소스를 확인해 보시면 될 것 같습니다.

  10. aszin 2010.12.24 09:04  댓글주소  수정/삭제  댓글쓰기

    안녕하세요~~ 파싱에 관한 자료를 찾다가 와서 강의 잘보고 있습니다.^^
    지금은 파싱을 해서 제목만 불러오는데요..제목을 터치하면 페이지가 바뀌면서 본문내용까지
    파싱을 해 오고 싶은데 어떠한 추가적인 작업을 해주어야 하나요??
    혹시 이에 관련된 강의를 해주실 수 있나요??
    가능하시면 부탁드립니다.
    연말연시 잘 보내시고요~~^^

  11. 당근구리 2011.03.02 10:54  댓글주소  수정/삭제  댓글쓰기

    좋은 포스트 잘 보고 갑니다.
    심플하게 정리 잘 하셨네요.
    퍼갈께요~

  12. slamdh 2011.03.22 17:51  댓글주소  수정/삭제  댓글쓰기

    마침 NSXMLParser로 rss읽어오는걸 찾고있었는데 좋은정보 감사드립니다.

    근데 하나 궁금해서 여쭈어볼까합니다.

    어떤싸이트는 되는데 어떤싸이트는 error code 31 이라는게 뜨는데 찾아보니

    EUC-KR을 지원안한다고해서 수정해보았는데 똑같은 에러가 뜹는데 혹시 알수있을까요?