Rss Reader Tutorial for iOS: How To Make A Simple RSS Reader iPhone App

Learn how to make a simple RSS reader in this iPhone app Tutorial. By Ray Wenderlich.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Sorting by Date

Most of the time when you’re looking at RSS entries, you want to see them listed in the order of the date the articles were posted, so you make sure you’re reading recent articles. So let’s get that working in this iPhone app tutorial!

It turns out that converting date strings in RSS/Atom is non-trivial, due to the different formats dates can potentially be written in. Luckily, Michael Waterfall has written a helper class that solves the problem! In fact, he’s written an entire RSS feed parser, but we’re obviously not going to use that here since we’re taking the approach of writing it ourselves.

But we do need that date converting class of his. So add it to your project by taking the following steps:

  1. Download MWFeedParser
  2. Drag Classes\NSDate+InternetDateTime.h/m to your Classes folder. Make sure “Copy items into destination group’s folder (if needed)” is checked, and click Add.

Then switch to RootViewController.m and make the following changes:

// Add to top of file
#import "NSDate+InternetDateTime.h"

// Replace line that sets articleDate to nil in parseRss
NSDate *articleDate = [NSDate dateFromInternetDateTimeString:articleDateString formatHint:DateFormatHintRFC822];

// Replace line that sets articleDate to nil ni parseAtom
NSDate *articleDate = [NSDate dateFromInternetDateTimeString:articleDateString formatHint:DateFormatHintRFC3339];

Compile and run the code, and you’ll see that the dates show up OK now! Now all you need to do is sort them.

But how do you do that? I’m sure many of you are familiar with sorting arrays using methods such as sortedArrayUsingDescriptor or sortedArrayUsingComparator, but what should you do if you want to simply insert a new item into the appropriate spot for an already-sorted array?

Well, there’s no built-in way to do this with the iOS API, but we can add a helper method to NSArray to make this a lot easier. Basically, it will use binary searching to find the appropriate spot inside a sortes NSArray to insert a new element, based on a passed-in comparator block.

So let’s add this helper method by creating a category on NSArray. Select the Classes group, go to File\New File, choose iOS\Cocoa Touch Class\Objective-C class, and click Next. Name the class NSArray+Extras.m, make sure “Also create NSArray+Extras.h” is checked, and click Finish.

Replace NSArray+Extras.h with the following:

//
// Adapted from: http://blog.jayway.com/2009/03/28/adding-sorted-inserts-to-uimutablearray/
//

#import <Foundation/Foundation.h>

@interface NSArray (Extras)

typedef NSInteger (^compareBlock)(id a, id b);

-(NSUInteger)indexForInsertingObject:(id)anObject sortedUsingBlock:(compareBlock)compare;

@end

And NSArray+Extras.m with the following:

//
// Adapted from: http://blog.jayway.com/2009/03/28/adding-sorted-inserts-to-uimutablearray/
//

#import "NSArray+Extras.h"

@implementation NSArray (Extras)

-(NSUInteger)indexForInsertingObject:(id)anObject sortedUsingBlock:(compareBlock)compare {
    NSUInteger index = 0;
    NSUInteger topIndex = [self count];
    while (index < topIndex) {
        NSUInteger midIndex = (index + topIndex) / 2;
        id testObject = [self objectAtIndex:midIndex];
        if (compare(anObject, testObject) < 0) {
            index = midIndex + 1;
        } else {
            topIndex = midIndex;
        }
    }
    return index;
}

@end

If you read through indexForInsertingObject, you can see that it compares the current object to the middle of the array, and recurses in either the top half or bottom half, depending on the sort result. This is a fast way to find the appropriate spot to insert an object into a sorted array.

You'll see that we set up the method so you can pass in a block of code that performs the comparison as a parameter, to make it nice and easy to use as well.

So let's give it a try! Open RootViewController.m and make the following changes:

// Add to top of file
#import "NSArray+Extras.h"

// In requestFinished, replace the line that sets insertIdx to 0 with this
int insertIdx = [_allEntries indexForInsertingObject:entry sortedUsingBlock:^(id a, id b) {
    RSSEntry *entry1 = (RSSEntry *) a;
    RSSEntry *entry2 = (RSSEntry *) b;
    return [entry1.articleDate compare:entry2.articleDate];
}];

Compile and run the code, and now the list of RSS entries should be sorted by date, and dynamically update as new articles come in!

Adding an element to a sorted NSMutableArray

Adding a Web View

Just one more finishing touch for this simple RSS reader - adding a web view to view a selected article! So let's quickly add this.

Select the Classes group in XCode, and go to File\New File, choose iOS\Cocoa Touch Class\UIViewController class, make sure "With XIB for User Interface" is checked but the others are not checked, and click Next. Name the class WebViewController.m, make sure "Also create WebViewController.h" is checked, and click Finish.

Open WebViewController.m and modify it to look like the following:

#import <UIKit/UIKit.h>

@class RSSEntry;

@interface WebViewController : UIViewController {
    UIWebView *_webView;
    RSSEntry *_entry;
}

@property (retain) IBOutlet UIWebView *webView;
@property (retain) RSSEntry *entry;

@end

This just adds an outlet for the web view we're about to add in Interface Builder, and a property to keep track of the RSS Entry to display.

Save your file. Then double click on WebViewController.xib and drag a web view from the library into the middle of the view so it fills up the whole area. Select the web view, and in the attributes inspector make sure "Scales pages to fit" is checked. Finally, control-click File's Owner, drag a line to the Web View, and connect it to the webView outlet.

Setting up Web View in Interface Builder

Next make the following changes to WebViewController.m:

// At top of file
#import "RSSEntry.h"

// Under @implementation
@synthesize webView = _webView;
@synthesize entry = _entry;

// Add new methods
- (void)viewWillAppear:(BOOL)animated {
 
    NSURL *url = [NSURL URLWithString:_entry.articleUrl];    
    [_webView loadRequest:[NSURLRequest requestWithURL:url]];
    
}

- (void)viewWillDisappear:(BOOL)animated {
 
    [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
    
}

// Add inside dealloc
[_entry release];
_entry = nil;
[_webView release];
_webView = nil;

These methods simply display the appropriate URL in viewWillAppear, and clear the screen on viewWillDisappear.

Next add a new instance variable and property for the WebViewController in RootViewController.h:

// Before @interface
@class WebViewController;

// Inside @interface
WebViewController *_webViewController;

// After @interface
@property (retain) WebViewController *webViewController;

Then make the following changes to RootViewController.m:

// At top of file
#import "WebViewController.h"

// Under @implementation
@synthesize webViewController = _webViewController;

// Replace tableView:didSelectRowAtIndexPath with the following
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    if (_webViewController == nil) {
        self.webViewController = [[[WebViewController alloc] initWithNibName:@"WebViewController" bundle:[NSBundle mainBundle]] autorelease];
    }
    RSSEntry *entry = [_allEntries objectAtIndex:indexPath.row];
    _webViewController.entry = entry;
    [self.navigationController pushViewController:_webViewController animated:YES];
                                
}

// In didReceiveMemoryWarning
self.webViewController = nil;

// In dealloc
[_webViewController release];
_webViewController = nil;

Compile and run your code, and you should now be able to click a post to see an article!

Viewing article in RSS Reader App