iPhone Rss Reader App IOS Tutorial 3: XML Parser in IOS

On January 6, 2013, in iPhone iPad Object-C, by James Liu

In this post, I will continue to show you how to implement an rss reader iphone app in Xcode. First, I need to clarify that there is no different between iPhone app and iPad app, except the UI design. If you are working in Xcode 4.2 or later, you can design the UI in storyboard. It gives us a directly visual design experience. This is the 3rd tutorial for iPhone Rss Reader app development, you can find all tutorials about this topics:

In previous post, HTTP Network Programming in IOS, I give an example to show how to make HTTP GET request and HTTP POST request. Via HTTP connection, we can get the RSS Feed content in XML format. To show the title in our table view, we need to parse xml into our data structure first.

In IOS SDK, we can use NSXMLParser to handle all xml parsing jobs. The reason we choose XML instead of JSON is that RSS 2.0 feed is defined by XML. Here is a piece of RSS feed content example.

<item>
<title>
iPhone Rss Reader App IOS Tutorial 2: HTTP Network Programming in IOS
</title>
<link>
http://jmsliu.com/1123/iphone-rss-reader-app-ios-tutorial-2-http-network-programming-in-ios.html
</link>
<comments>
http://jmsliu.com/1123/iphone-rss-reader-app-ios-tutorial-2-http-network-programming-in-ios.html#comments
</comments>
<pubDate>Sat, 15 Dec 2012 11:12:03 +0000</pubDate>
<dc:creator>James</dc:creator>
<category>
<!&#91;CDATA&#91; iPhone iPad Object-C &#93;&#93;>
</category>
<category>
<!&#91;CDATA&#91; IOS Programming &#93;&#93;>
</category>
<guid isPermaLink="false">http://jmsliu.com/?p=1123</guid>
<description>
<!&#91;CDATA&#91;
To create a complete rss reader iphone app, http request and response are both necessary modules. Website, for example, WordPress powered website, usually provides rss feed feature. Hence, using our rss reader iphone app can easily get the rss feed contents from Http request. HTTP network programming in IOS is very simple. NSURLConnection class and &#91;...&#93;
&#93;&#93;>
</description>
<wfw:commentRss>
http://jmsliu.com/1123/iphone-rss-reader-app-ios-tutorial-2-http-network-programming-in-ios.html/feed
</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>

If you want to know what the whole RSS feed looks like, you can type following link in your browser to check:

http://jmsliu.com/feed

Using NSXMLParser to parse XML is very simple. As an example, I will create a class RssXMLParser to handle the xml parsing job. RssXMLParser will implement the delegate NSXMLParserDelegate. Hence, the RssXMLParser.h will look like:

#import <Foundation/Foundation.h>

@interface RssXMLParser : NSObject <NSXMLParserDelegate>
{
    //for switch and case
    enum nodes {title = 1, postlink = 2, pubDate = 3, invalidNode = -1};
    enum nodes aNode;

    //for holding the parsing result
    NSMutableDictionary *articles;
    
    //for matching the article title and link
    NSString *lastTitle;
}

-(void) parseRssXML: (NSData *)xmldata;
@end

In RssXMLParser.m, I will implement and override the following functions:

  • – (void) parseRssXML:(NSData *)xmldata
  • – (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
  • – (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
  • – (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string

The function parseRssXML is defined by us, which will be called by view controller. The functions didStartElement, didEndElement, foundCharacters are defined by NSXMLParserDelegate. They will be called when NSXMLParser starts to parse the xml. The following is the example of RssXMLParser.m.

#import "RssXMLParser.h"

@implementation RssXMLParser

- (void) parseRssXML:(NSData *)xmldata
{
    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:xmldata];
    [xmlParser setDelegate:self];
    [xmlParser setShouldResolveExternalEntities:NO];
    [xmlParser parse];
}

- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if([elementName isEqualToString:@"item"])
    {
        aNode = invalidNode;
        if(articles == nil)
        {
            articles = [[NSMutableDictionary alloc] init];
        }
    }
    else if([elementName isEqualToString:@"title"])
    {
        aNode = title;
    }
    else if([elementName isEqualToString:@"link"])
    {
        aNode = postlink;
    }
    else
    {
        aNode = invalidNode;
    }
}

- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if( [elementName isEqualToString:@"rss"] )
    {
        NSLog(@"xxxxx");
    }
}

- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    switch (aNode) {
        case title:
        {
            string = [string stringByTrimmingCharactersInSet:[NSCharacterSet nonBaseCharacterSet]];
            string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
            if(string.length != 0)
            {
                lastTitle = string;
            }
        }
        
        break;
        case postlink:
        {
            string = [string stringByTrimmingCharactersInSet:[NSCharacterSet nonBaseCharacterSet]];
            string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
            if(string.length != 0 && articles != nil)
            {
                [articles setObject:string forKey:lastTitle];
            }
        }
        break;
            
        default:
            break;
    }
}

@end

After NSXMLParser finished to parse the XML, the function didEndElement will be called. Now we have a new problem. Because parsing XML is unblocking process, it will process in different thread. Therefore, you don’t know when it will finish the whole parsing process after you call NSXMLParser parse. There are two ways to solve the problem.

  • Implement the NSXMLParserDelegate in view controller
  • Define a new delegate and let RssXMLParser call the delegate once it finished parsing job

In the first way, we implement the NSXMLParserDelegate in view controller. In our example, we can implement NSXMLParserDelegate in RssTableViewController. This way is much simple to code. However, it is not the best way in software design perspective. If we implement NSXMLParserDelegate in RssTableViewController, RssTableViewController will handle all xml parsing job, as well as view logical control. To separate the job responsibility, we choose RssXMLParser to manage the xml parsing logic. Therefore, the second way is much better.

In the second way, we define a delegate in RssXMLParser. In the delegate, there is only one function. We design RssXMLParser to call this function after it finishes the parsing. Hence, the new version of RssXMLParser.h will look like:

#import <Foundation/Foundation.h>

@protocol XMLParserDelegate;

@interface RssXMLParser : NSObject <NSXMLParserDelegate>
{
    //for switch and case
    enum nodes {title = 1, postlink = 2, pubDate = 3, invalidNode = -1};
    enum nodes aNode;

    //for holding the parsing result
    NSMutableDictionary *articles;
    
    //for matching the article title and link
    NSString *lastTitle;
}

@property (assign, nonatomic) id<XMLParserDelegate> delegate; 

-(void) parseRssXML: (NSData *)xmldata;
@end

@protocol XMLParserDelegate <NSObject>

@required
- (void) onParserComplete: (NSObject *) data XMLParser: (RssXMLParser *) parser;
@end

After we add the delegate variable in RssXMLParser.h, we can update the RssXMLParser.m as well.

@synthesize delegate;

- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if( [elementName isEqualToString:@"rss"] )
    {
        [delegate onParserComplete:articles XMLParser:self];
    }
}

Then, we can implement the XMLParserDelegate in the RssTableViewController. Here is RssTableViewController.h,

#import <UIKit/UIKit.h>
#import "RssXMLParser.h"

@interface RssTableViewController : UITableViewController <NSURLConnectionDataDelegate, XMLParserDelegate>
{
    NSMutableData *httpReceivedData;
}

@property (retain, nonatomic) NSMutableArray *rssData;
@end

In RssTableViewController.m, we will update the function viewDidLoad, connectionDidFinishLoading and override function onParserComplete.

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    rssData = [NSMutableArray array];
    
    RssHttpController *httpController = [[RssHttpController alloc] init];
    [httpController getRSSContent:[[NSNumber numberWithInt:1] stringValue] rssurl:@"http://jmsliu.com/feed?paged=" delegate:self];
}

- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSString *result = [[NSString alloc] initWithData:httpReceivedData encoding:NSUTF8StringEncoding];
    NSLog(@"%@", result);
    
    RssXMLParser *rssXMLParser = [[RssXMLParser alloc] init];
    [rssXMLParser setDelegate:self];
    [rssXMLParser parseRssXML:httpReceivedData];
}

- (void) onParserComplete:(NSObject *)data XMLParser:(RssXMLParser *)parser
{
    [parser setDelegate:nil];
    
    articlesList = (NSMutableDictionary *)data;
    NSArray *keyList = [articlesList allKeys];
    
    for (NSString *title in keyList)
    {
        [rssData addObject:title];
    }
    
    [self.tableView reloadData];
}

Now, let’s see the app result in my iphone.
UITableViewCell Default UI

Here are the first 5 articles in my website. To make the title nicer, we can do some simple customization in UITableViewCell. Here is a post to show you how to customize UI Table Cell View. After making some changes, the new UI looks like:
UI Table View Cell Two Line Text

Add More Features in iPhone Rss Reader

After this tutorial, we already accomplish showing the website content in table view list. There are two more functions we have to add. Like other new reader app, pull down and refresh the content will be a necessary function. I recommend you to read another post: Pull Down to Refresh Tablet Content. It will show you how to add pull down to refresh component in this Rss Reader example.

Another feature we have to add is clicking the list item and show the real content in a web view. It will help users to read the details about the story, not just the title. In the next post, Show Rss Feed in UIWebView, I will add this feature in our iPhone Rss Reader example.

Tagged with:  

17 Responses to “iPhone Rss Reader App IOS Tutorial 3: XML Parser in IOS”

  1. san says:

    Hello, great tutorial.. only can u post a sample project with all the necessary files. You kind of left a puzzle and Im gtting constant error messages thanks

  2. bob says:

    where is the RssHttpController.h?

  3. Jes says:

    Hi. I am following your guide. Thanks for it. One question, how to to a webview of the rss feed when clicked?

    Thanks james

    • xMac says:

      Yeah iwant to know this, too!
      I don`t understand how to Copy the link from the rssfeed and open it in a web view.
      Please help me!

      Thanks
      xMac

  4. Mohamed says:

    Hi,

    Thanks for the tutorial. What about using other websites like twitter for RSS feeds? I tried substituting the link you provided with a RSS version link of a twitter account and it doesn’t work for me. I’m a newbie by the way.

    Thanks

    • James says:

      You need to debug to see what the return value from twitter. If the xml returned by rss doesn’t match the wordpress rss feed, it will failed. In this case, you need to change RssXMLParser class to parse the twitter rss data.

      Thanks

      • Mohamed says:

        Thanks for the feedback. Looking at both XMLs, they are different. I modified the variables in the “if” statements in RssXMLParser.m. So instead of using “item”, “tittle”, “link”, and “rss”, I used the ones for the twitter XML (“status”, “text”, created_at”, “statuses”). Still got nothing.

        Any help is appreciated.

        FYI – the twitter XML I’m using is: http://api.twitter.com/1/statuses/user_timeline/nba.xml

        • James says:

          Hi Mohamed,

          If you are using the given xml, you are right. You need to change the following tags:

          item to statuses
          title to text
          link to source

          After that, the app will work for your xml. By the way, the twitter rss feed link will be like:
          https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=nba

          With this link, the xml format will be different from your provided xml. If you want to create app to read twitter rss, you’d better to use the above rss link.

          Thanks

          • Mohamed says:

            Thanks! Any reason why the other link wouldn’t work? Or would it work by changing the tags?

            By the way, I’m just wondering where you got that other feed link. Is there a way to convert any website to xml format?

        • James says:

          Hi Mohamed,

          Here is a new post, which will help you to read Twitter RSS.

          Thanks

        • James says:

          Hi Mohamed,

          The RSS Feed XML format is a standard. So you can use this app to read rss feed from any websites which use the standard rss feed.

          Thanks

          • Mohamed says:

            Is there a way I can make “rssurl” multiple websites, instead of only one? An array of websites or something like that?

            Thanks

    • James says:

      Hi, Mohamed

      If you want to read multiple rss from different websites. It is doable. It is basing on how you design your app, for example, UI and function.

      a. You can merge your rss from all the website you want. In this way, you can store all data in the rssData
      b. You can design the UI to read rss separately. If you choose, you can maintain several rssData array to store the rss.

  5. Simon says:

    Really useful tutorial. When I go to input my RSS feed, there is nothing in the table cells. There are no errors in the output of the RSS feed, so am stumped at what the problem could be. The only thing is that it specifies a min width of 740px. Any thoughts?

    Thanks

    • James says:

      Hi Simon,

      First, I think you can check if you get the data from your rss feed. You can debug in connectionDidFinishLoading function. After that, you’d better to check if the rss parser works. You can check it in onParserComplete function. Please check there is data in rssData.

      Thanks

Leave a Reply

Premium WordPress Themes

Premium WordPress Themes