RSS Reader using NSXMLParser

I was working on building a slick RSS reader for my iphone since last week. As you might guess, the core problem in building an RSS reader is to create a way to read the XML feed data. For a newcomer, this might sound straigtforward but once they rub nose with it, it would soon be clear that the various forms of feeds that are available now, makes it a tricky task. Above that, iPhone does not support the good guys of XML world, namely NSXMLDocument etc. This class in the Cocoa framework is really helpful with its XPath support to parse XML feeds easily. The NSXMLParser is the sole fighter in the XML arena on the iPhone device (as far as I know). Some folks have tried building wrapper classes to support iPhone, an example is TouchXML which is available at http://code.google.com/p/touchcode/. The class used by this method is CXMLDocument which is kinda on the same lines of NXMLDocument, but with much lesser support for parsing. It turned out that it was not good enough for what I needed to do.

So… I wrote a NSXMLParser! Now lets be honest here, not many people are lovers of Event driven xml programming and I am no exception. It just seems really tedious to take care of all the conditions in such a model when used for parsing. Enough bashing, here is some code to show how it can be used to parse an XML feed at a url.

- (NSMutableArray *)parseXMLFeedAtURL:(NSURL *)URL parseError:(NSError **)error 
{
channelFlag = NO;
mainTitleFlag = 0;
itemFlag = 0;
linkFlag = 0;
feedFlag = NO;
entryFlag = 0;
appDel = (iBlogReaderAppDelegate *)[[UIApplication sharedApplication] delegate];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[parser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
NSError *parseError = [parser parserError];
if (parseError && error) {
*error = parseError;
}
[parser release];
return tempArray;
}


- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict 
{
if (qName) {
elementName = qName;
}
if([elementName isEqualToString:@"channel"])
{
// Seems like a normal RSS feed.
channelFlag = YES;
return;
}

// More code here.

}


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

// More code here.

}


- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string 
{
if(tempArray != nil)
{
if(charString == nil)
{
charString = [[NSMutableString alloc] init];
}
if(mainTitleFlag == 1 || linkFlag == 1)
{
[charString appendString:string];
}
}
}


The above function acceps a NSURL object which is created using a url string and a pointer to pointer to an NSError object. To use NSXMLParser, the best thing would be to create a new NSObject class. In that class add the above methods so that this implementation stays away from your other application code. NSXMLParser needs didStartElement, didEndElement and foundCharacters methods to read values within elements. More methods are available if you refer to the reference library provided by Apple.



Every time a new tag is encountered, the didStartElement event is triggered, when it ends didEndElement is triggered, and when characters are found between tags the foundCharacters event is triggered. So you see the main issue is here to maintain the flow across events. It is very common to have nested “title” elements in an RSS feed, so care must be taken while parsing them. I use flags to check if certain element has been encountered by the parser or not. Well this method may vary according to your needs. In my app, I support both Atom and RSS feeds so I had to give option for the user to select which feed they need to subscribe to. Below is a snap of that view.



Picture 1



On Done, I loop through the selection list and parse each feed using my parser. Now the way I got these three feeds is by parsing the url entered in the textbox. It would be something like



<link rel="alternate" type="application/rss+xml" title="RSS" href="http://feeds.feedburner.com/codinghorror/" />


I would like to share my XCode project if anyone needs it, although I am not sure where can I upload those files. But that can be figured out later. Let me know if you need any help is using the infamous NSXMLParser :).



Happy Programming!



 

Abhang Rane


4 comments :

Reader's Comments

  1. i would like to see the rest of the code if thats okay ?

    ReplyDelete
  2. I would like to see the code/project if that is possible

    ReplyDelete
  3. Your RSS reader looks good. I'm just following some tutorials now - Any chance I can take a look @ your code? Thanks!

    My email address is jayjardim@mac.com - I'm just using it for learning purposes. Thanks a bunch!

    ReplyDelete