If my blog helped you and your business, I'd like to ask you to help me raise money to secure drinking water to 25 people in a developing country.
It's charity, good karma and a chance to give back to the people. Learn more.
Tutorial: Building advanced RSS reader

RSS has gained recently a lot of popularity, especially due to the boom of blog creation. The RSS format and the idea of web content syndication goes back to 1999 and that idea didn’t change drastically since then.

Let’s take a quick look at an RSS feed example head:

<channel>
<title>Touch Code Magazine</title>
<link>http://www.touch-code-magazine.com</link>
<description>Tutorials and excerpts about iPhone and iPad programming in Objective-C and Cocoa Touch</description>
<language>en</language>

The RSS items are wrapped in a channel tag which contains also general information about the feed like description of the feed, URL of the source site and others. Inside the channel tag are found the items of the feed, like in the example below:

<item>
<title>Payment requests are restricted to products returned as valid via Store Kit’s didReceiveResponse method.</title>
<description><![CDATA[After working for a long time on a project having a lot of different in app purchases, now I am back to a new one which[...]]]></description>
<link>http://www.touch-code-magazine.com/payment-requests-are-restricted-to-products-returned-as-valid-via-store-kits-didreceiveresponse-method/</link>
</item>

So, today we are going to build an iPhone which loads a remotely located RSS feed and shows the contents to the user.

There has been a lot of examples around the Internet of how to build RSS reader app, but I want to showcase few things with this tutorial:

  • how to separate the logic and have standalone RSS loader class which communicates to a delegate class
  • how to start customizing a table view
  • how to have some custom logic tableview cells
  • how to show different content inside the table view depending on the state of the table
  • how to use Google’s library for parsing the xml to gain some speed gain compared to NSXML

So, with this in mind let’s dive into the project.

Building RSS reader

You can either follow the instructions or alternatively download the demo project right now and read the tutorial trough and follow the code in the demo project I already wrote.

Start XCode and create a new project – choose Navigation-based Application.

XCode creates for you the RootViewController class which holds already the necessary infrastructure for having an index table for your feed contents. Now double click RootViewController.xib (in the folder Resources) to open up Interface Builder. You can see the view of this controller is a table view (i.e. on this screen of the application the table is the root view)

In the Inspector (shift+cmd+I) make sure the table view style is “Grouped” and separator is “None”. While in the Inspector also check that only vertical scrolling is enabled. Now the table view should look like that :

Besides the root view controller which will show the contents of the feed you will need one extra view controller to show the actual contents of the linked articles. Create a new view controller in your XCode project (I find it the easiest to right-click on the “Classes” folder and choose “Add” and “New file”) and name it “DetailsViewController” – choose also to have a xib file created for that view controller.

Cocoa Touch provides you with a class to parse XML – NSXML, but it is famous for being not really performant, so for this project you will need to download an alternative XML parsing class from Google. Open the GData project from Google : http://code.google.com/p/gdata-objectivec-client/downloads/list and download the gdata-objectivec-client-1.10.0.zip. Inside the archive you will find a folder called Support/XMLSupport. Copy the GDataXMLNode.h and GDataXMLNode.m inside your project.

To process XML you’ll need to change the project’s configuration:

  1. Right click your project item in the project tree and select “Add existing frameworks…” from the list select “libxml2.dlyb”
  2. Open the project properties, in the Build tab find the setting called “Other linker flags” add one flag and enter “-lxml2”
  3. In the same list find the setting called “Header search paths” and add one new path and enter “/usr/include/libxml2”, uncheck “recursive” if checked

This will include the xml library to your project.

You are now ready to lay down the RSS loader class. Create a new class called RSSLoader.
In the .h file add these definitions to the top :

#import "GDataXMLNode.h"
#define kRSSUrl @"http://feeds.feedburner.com/TouchCodeMagazine"

For the RSS loader you will need few methods and to make everything shiny you will also need some methods for the RSS loader’s delegate:

@protocol RSSLoaderDelegate
@required
-(void)updatedFeedWithRSS:(NSArray*)items;
-(void)failedFeedUpdateWithError:(NSError*)error;
-(void)updatedFeedTitle:(NSString*)title;
@end
 
@interface RSSLoader : NSObject {
UIViewController<RSSLoaderDelegate> * delegate;
BOOL loaded;
}
 
@property (retain, nonatomic) UIViewController<RSSLoaderDelegate> * delegate;
@property (nonatomic, assign) BOOL loaded;
 
-(void)load;
 
@end

The delegate will be the root view controller, so when the feed is loaded it will be messaged to get the feed items and load them into the index table. updatedFeedWithRSS will be called upon successful loading of the feed, failedFeedUpdateWithError will be called when there has been an error loading the feed. updatedFeedTitle will set the feed title in the delegate’s view.

The protocol is pretty simple but helps keeping the relationship with the delegate pretty tight. In a big project having proper protocols is an absolute must – otherwise things can get messy pretty quick.

Let’s now have a look at the implementation of the loader class. The only public method is load:

-(void)load
{
[self dispatchLoadingOperation];
}

It will only call another method, which will then create a loading operation (more on operations in a moment). Why have a method with only one line of code, which calls another method? There might be also other cases in the future development of the class which will need to fire a loading operation – for example if you add a refresh method, which gets called every 5 minutes or so – then refresh will have to update the UI and call the loading operation. By separating early the quite concrete load method and the firing of the operation you lay down grounds for abstraction and scalability for your class.

For each task that might be blocking the UI you need to use threads. Loading a remote file from a server is one of those cases; and the easiest way to perform threaded execution is the NSInvocationOperation. Let’s have a look a the dispatchLoadingOpearation method:

-(void)dispatchLoadingOperation
{
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(fetchRss) object:nil];
[queue addOperation:operation];
[operation release];
[queue autorelease];
}

In just a few lines a new InvocationOperation is created with the selector fetchRss and the thread is dispatched to the operation queue- the fetchRss method of the class will be ran into a separate thread from the UI – exactly what is needed.

Now in fetchRss you will need to load remote content from the URL you specified in the kRssFeed and load it in a XML document. Let’s have a look at how to do that (don’t worry RSSLoader is almost finished and you’ll get to see some action very soon)

-(void)fetchRss
{
NSLog(@"fetch rss");
NSData* xmlData = [[NSMutableData alloc] initWithContentsOfURL:[NSURL URLWithString: kRSSUrl] ];
NSError *error;
 
GDataXMLDocument* doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&amp;error];
 
if (doc != nil) {
self.loaded = YES;
 
GDataXMLNode* title = [[[doc rootElement] nodesForXPath:@"channel/title" error:&amp;error] objectAtIndex:0];
[self.delegate updatedFeedTitle: [title stringValue] ];
 
NSArray* items = [[doc rootElement] nodesForXPath:@"channel/item" error:&amp;error];
NSMutableArray* rssItems = [NSMutableArray arrayWithCapacity:[items count] ];
 
for (GDataXMLElement* xmlItem in items) {
[rssItems addObject: [self getItemFromXmlElement:xmlItem] ];
}
 
[self.delegate performSelectorOnMainThread:@selector(updatedFeedWithRSS:) withObject:rssItems waitUntilDone:YES];
} else {
[self.delegate performSelectorOnMainThread:@selector(failedFeedUpdateWithError:) withObject:error waitUntilDone:YES];
}
 
[doc autorelease];
[xmlData release];
}

GDataXMLDocument loads and parses the text contents of the RSS feed. Then as seen on the top of the article the title and items are located in the channel tag. When fetched we pass the title to the root view controller to update the UI (stringValue returns the text contents of an XMLNode):

[self.delegate updatedFeedTitle: [title stringValue] ];

Afterwards with another XPath query “channel/item” we get all feed items into the items array and we can iterate over them to load all data needed with the getItemForXmlElement method:

-(NSDictionary*)getItemFromXmlElement:(GDataXMLElement*)xmlItem
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[[[xmlItem elementsForName:@"title"] objectAtIndex:0] stringValue], @"title",
[[[xmlItem elementsForName:@"link"] objectAtIndex:0] stringValue], @"link",
[[[xmlItem elementsForName:@"description"] objectAtIndex:0] stringValue], @"description",
nil];
}

For the complete source code of the RSSLoader download the demo application and have a look inside. Now after building the RSSLoader you are also ready to connect it with the main view controller.

Modify your root view controller to look like the following:

#import <UIKit/UIKit.h>
#import "RSSLoader.h"
#import "DetailsViewController.h"
 
@interface RootViewController : UITableViewController<RSSLoaderDelegate> {
RSSLoader* rss;
NSMutableArray* rssItems;
}
 
@end

You will keep the list of items and RSSLoader instance as ivars, so you can access them anytime. Also notice you need to tell that RootViewController will have to comply to the RSSLoaderDelegate protocol.

To understand better the lifecycle of the RSS reader you are going to create I need to explain the two distinct states in this lifecycle. At first the application does not have any data to show, so you will need to make sure the table view is letting the user know a loading process is taking place; second when the updateFeedWithRSS method is called the table view needs to load the items and prepare for interaction with the user.

Inside the RootViewController lay down the initialization methods:

- (void)viewDidLoad {
[super viewDidLoad];
 
self.navigationItem.title = @"Advanced RSS Reader";
rssItems = nil;
rss = nil;
 
self.tableView.backgroundColor = [UIColor clearColor];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.tableView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
 
}
 
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
 
if (rss==nil) {
rss = [[RSSLoader alloc] init];
rss.delegate = self;
[rss load];
}
}

This way you allow the view to be rendered, the title of the controller to show the name of the application and when ready to start the RSSLoader loading method.

Once the RSSLoader is ready fetching the items it will call the delegate’s methods. Let’s start by having the success method and placeholders for the rest of them:

#pragma mark -
#pragma mark RSSLoaderDelegate
-(void)updatedFeedWithRSS:(NSMutableArray*)items
{
rssItems = [items retain];
[self.tableView reloadData];
}
 
-(void)failedFeedUpdateWithError:(NSError *)error
{
//
}
 
-(void)updatedFeedTitle:(NSString*)rssTitle
{
//
}

As you see when you get and store the feed items, you call the reloadData method on the tableView, which will then re-create the table cells and you have the chance to provide the freshly loaded data to it.

To make the content index more interesting and informative, we are going to make a zebra table – the even cells will show the title of the RSS articles and the odd cells will show preview of the content (found in the description tag inside the RSS). To have that we need some more advanced cell creation.

Let’s start by laying down the cellForRowAtIndex method, which will have few different behaviors : when the RSS is still loading it will return a single cell which shows a progress indicator, when the RSS is loaded it will return a title cell or a description cell depending on the row number inside the table. Let’s get started:

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
if (rss.loaded == NO) {
return [self getLoadingTableCellWithTableView:tableView];
}
 
if (indexPath.row % 2 == 1) {
return [self getTextCellWithTableView:tableView atIndexPath:indexPath];
}
 
static NSString *CellIdentifier = @"TitleCell";
 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
 
UIView *backView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
backView.backgroundColor = [UIColor clearColor];
cell.backgroundView = backView;
 
NSDictionary* item = [rssItems objectAtIndex: indexPath.row/2];
 
cell.textLabel.text = [item objectForKey:@"title"];
 
return cell;
}
 
- (UITableViewCell *)getLoadingTableCellWithTableView:(UITableView *)tableView
{
static NSString *LoadingCellIdentifier = @"LoadingCell";
 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:LoadingCellIdentifier];
 
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:LoadingCellIdentifier] autorelease];
}
 
cell.textLabel.text = @"Loading...";
 
UIActivityIndicatorView* activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activity startAnimating];
[cell setAccessoryView: activity];
[activity release];
 
return cell;
}
 
- (UITableViewCell *)getTextCellWithTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
static NSString *TextCellIdentifier = @"TextCell";
 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TextCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:TextCellIdentifier] autorelease];
}
 
NSDictionary* item = [rssItems objectAtIndex: (indexPath.row-1)/2];
 
//article preview
cell.textLabel.font = [UIFont systemFontOfSize:11];
cell.textLabel.numberOfLines = 3;
cell.textLabel.textColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
 
UIView *backView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
backView.backgroundColor = [UIColor clearColor];
cell.backgroundView = backView;
 
CGRect f = cell.textLabel.frame;
[cell.textLabel setFrame: CGRectMake(f.origin.x+15, f.origin.y, f.size.width-15, f.size.height)];
cell.textLabel.text = [item objectForKey:@"description"];
 
return cell;
}

getLoadingCellWithTableView will provide the table view with a single cell during the loading phase – just some text and an activity indicator. Then when the RSS is loaded table view will receive one title cell, one description cell, one title cell, one description cell, etc.
Now for this to work you will have to instruct the table you have 1 row during the loading phase and 2 times the number of the actual articles in the feed when the RSS is loaded.

Have a look at this method and add it to the RootViewContrller:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (rss.loaded == YES) {
return [rssItems count]*2;
} else {
return 1;
}
}

Ok, now if all is good when you run the project you should see the RSS content index like that:

Pretty neat ! Everything works well together – the RootViewController sets up the UI, calls the RSSLoader, which loads the remote content in a separate thread and then lets the RootViewController know that it needs to update the UI with the content.

Next page of "Tutorial: Building advanced RSS reader"



Marin Todorov

is an independent iOS developer and publisher. He's got more than 18 years of experience in a dozen of languages and platforms. This is his writing project.
» Marin's homepage    » Contact    » Marin's Cocos2D game creation course

  1. Chuck on Tuesday 21, 2010

    Great article!, I also like you used GData I find it easier to use than NSXML in iOS.

  2. Marin on Tuesday 21, 2010

    Thanks Chuck, yes I’ve been looking into different benchmarks of XML parsing libraries and GData is not the fastest, but it’s a fine mesh of performance, lightness and ease of use. Thanks a lot for the comment

  3. [...] Tutorial: Building advanced RSS reader RSS Reader (Part 1): Getting started with iPhone Development RSS Reader (Part 2): Creating a new iPhone Xcode project and setting up your environment RSS Reader (Part 3): What we’ll be building and which iPhone SDK features will be covered? RSS Reader (Part 4): Setting up the UITabBar and UITableView with delegates [...]

  4. Robert on Tuesday 21, 2010

    Great article, was wondering where does the fetchRSS method go and how it relates to muti threading

    Cheers

  5. Marin on Tuesday 21, 2010

    Hi Robert,

    have a look at the method called “dispatchLoadingOperation” it creates a new thread using the selector fetchRSS (in the web site you need to scroll a bit horizontally to see the code)

    [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(fetchRss) object:nil]

    this basically creates a new thread which when started will execute the fetchRss selector – have a look at the code of dispatchLoadingOperation it’s everything you need to spawn a new thread

  6. bob on Tuesday 21, 2010

    [doc autorelease];

    Why are you autoreleasing this? Why not just release it outright?

  7. Marin on Tuesday 21, 2010

    Hi Bob,

    good question – I honestly don’t see why it is autoreleased, I might’ve had this code elsewhere, where it made sense, but the way it’s now definitely an immediate release is better.
    Thanks for noticing!

    Marin

  8. Dave on Tuesday 21, 2010

    Hi Martin,

    Thanks for sharing, i’ve learnt a lot regarding threading from this. I’m new to objective-c so excuse me if im asking a silly question. I’m making some changes to this so that the data is stored to the DB and only runs fetchRss providing db is empty or lastUpdateTime > n hours.

    Ive hit a brick wall and im not sure how to go about solving it. You’ve set RootViewController to comply RSSLoaderDelegate protocol. I want to change it so the appDelegate deals with the fetching of the rss and storing to the db and my viewcontroller to read from the DB only. Im running into a heap of problems. Could please advise how is a sensible thing to do? if not, could you please steer me in the right direction?

    Thanks,

    Dave

  9. Marin on Tuesday 21, 2010

    hi dave i’m glad the tutorial was oh help to u :)
    first of all have a look what libraries there are for communicating with a database – the native c functions are a nightmare – i think there is one called fdbm which is quite easy to use if you know some sql.

    i’d say when fetching rss items store them in the database and then store the current time (using nsdate) into the user defaults. when you finish fetching and saving the items call perforomSelector withObject withDelay to set a timeout for the next fetch.

    in general maintainig an rss feed stored in a database is much more complicated task than just fetching the feed as in the tutorial. have a look for another good tut on how to deal with the db

    when im back from the road would be a good idea to do a tut on database interaction from obj ckeep an eye on my rss;)

  10. Colin on Tuesday 21, 2010

    Hi,

    I’m a newbie to iPad development and would like to transform this using a uisplitviewcontroller. I’ve tried to make this work but can’t really get it to go. Do have an example of how to do this?

  11. Marin on Tuesday 21, 2010

    Hi Colin,

    don’t have an example straight away, but it should be pretty straight forward to turn the tableviewcontroller to a uisplitviewcontroller solution, maybe just google for “uisplitviewcontroller tutorial” …

    best of luck, marin

  12. James on Tuesday 21, 2010

    hi everybody

    i have a question that i hope one of you would be able to answer.
    my question is How would i be able to add a reload or refresh button to this application to fetch new information.

    thanks James

  13. mark12 on Tuesday 21, 2010

    Hello!
    Thanks for the tutorial, I liked it a lot.
    But I have some problems. First of all I’m using a tab bar application. When I ran the app in ismulator and tap on a feed, it doesn’t open the webview(detailsview). If you cant tell me your iChat(aim) or anything else so we can talk it would be nice :) )
    Thanks for your time!

  14. Marin on Tuesday 21, 2010

    Hey, glad you liked the RSS tutorial! Now – if when you implement it inside your own app going between view controllers doesn’t work it means you probably didn’t copy over the code correctly – the scope of the tutorial is anyways to show you fetching RSS feeds, not insights of building iPhone apps :) For more details on using navigation controller there’s a ton of other tuts out there: http://www.google.com/search?client=safari&rls=en&q=uinavigationcontroller+tutorial&ie=UTF-8&oe=UTF-8

  15. Kapalla on Tuesday 21, 2010

    Very good article!

    best regards from germany!

  16. Marin on Tuesday 21, 2010

    Moin moin :)

  17. nolan on Tuesday 21, 2010

    Hi

    great tutorial and app, I changed the rss feed to pull in a youtube feed of funny videos feed://gdata.youtube.com/feeds/base/videos?q=funny&client=ytapi-youtube-search&alt=rss&v=2

    It works fine but I want to be able to show the thumbnails in the tableview, is this possible to do in your app. One more question to have you modified this app for xcode 4.2 in regards to ARC?

  18. Marin on Tuesday 21, 2010

    hey nolan! 1) you’ll have to show the thumbnails yourself, this tutorial shows only fetching the RSS feed 2) you can disable ARC per file, have a look here how to: http://maniacdev.com/2012/01/easily-get-non-arc-enabled-open-source-libraries-working-in-arc-enabled-projects/ … best, Marin

  19. Ganesh on Tuesday 21, 2010

    I have an RSS link in which there are rss links to pages such as AFL,Football, Basketball,Cricket etc. When I click that the RSS links are obtained. For example in case of AFL the rss link directs to the page

    http://www.sportal.com.au/rss/australia/afl.xml

    Here there are tags called “description” but there is only a part of description inside that tag. The full description is present inside the link present inside the tag “link”. Here for example the link is

    http://www.sportal.com.au/afl-news-display/pies-duo-set-to-feature-160240

    How can I get these full description from the above link? I’m in such a urgency. Any help is appreciated.

    I have used the MWFeedParser. But still the problem exists. The problem is about the contents of parsing my friend. Inside the description tag there is no full description. Only part of it is given. But i need the full description. The full description is present inside the weblink inside the “link” tag. I need to get that data. How can i do that?

  20. Marin on Tuesday 21, 2010

    Hi Ganesh, you correctly figured out this is not an RSS issue. You’ll need to fetch the web page HTML code and then strip out the content that is interesting for you. Unfortunately there’s no simple way to do that. The webpage will contain more information which is not related to the post at all – like sidebar, menus, navigation, adverts, etc.

  21. Damien on Tuesday 21, 2010

    Hi!
    Thank you for this great tutorial. It is simple and great. I was wondering how to implement a pull down method to refresh the view. I found a method on waterworld.com blog. Here is the code to reload the page:

    -(void) pullDownToReloadActionFinished {
    // add one data on top
    [rssItems insertObject:[NSNumber numberWithInt: rand()] atIndex:0];
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject: [NSIndexPath indexPathForRow:0 inSection:0]]
    withRowAnimation:UITableViewRowAnimationTop];
    [self.pullToReloadHeaderView setLastUpdatedDate: [NSDate date]];
    [self.pullToReloadHeaderView finishReloading:self.tableView animated:YES];
    }

    -(void) pullDownToReloadAction {
    // finish reload after three seconds
    [self performSelector:@selector(pullDownToReloadActionFinished) withObject:nil afterDelay: 3.0f];
    }

    But I can’t figure out how to just reload the rss feed! Can someone help me, please?
    Thank you

  22. John on Tuesday 21, 2010

    Love your tutorial. Can you help me implement a search bar for information displayed in the tables? I’ve now used this for one app and 3 unique RSS feeds – If we can get the search to work, I would love to make a donation…

    John

  23. Oliver on Tuesday 21, 2010

    You write :

    GDataXMLNode* title = [[[doc rootElement] nodesForXPath:@”channel/title” error:&error] objectAtIndex:0];

    –> So here, nodesForXPath is asusmed to return an array of nodes.

    NSArray* items = [[doc rootElement] nodesForXPath:@”channel/item” error:&error];
    for (GDataXMLElement* xmlItem in items) {
    [rssItems addObject: [self getItemFromXmlElement:xmlItem] ];
    }

    –> So here, nodesForXPath is asusmed to return an array of elements.

    As GDataXMLElement inherits GDataXMLNode, and nodesForXPath returns an array of GDataXMLNode, how can you just write the last statement assuming it’s an array of GDataXMLElement ?