Iphone Tutorial: Creating a RSS Feed Reader

Iphone Tutorial: Creating a RSS Feed With Podcast Part 1

Creating an RSS reader for iphone is a great way to learn about the language. In this tutorial we’re going to create a rss reader that can also play podcasts. Such application will require us to download and parse a rss feed, display the data in a table and play a remote podcast. In this article I assume a minimal knowledge of cocoa programming, but you should be able to follow even if this is the first time you’re coding an iphone application.

Update: Fixed some issues and upgraded the code to IOS4

The final application will look like this:

In order to develop an iphone application, you need to install the cocoa touch sdk. You can download it from here if you haven’t already. Once you’ve done that, you’re ready to get started.

Project setup

Open up xcode, and create a new project of type Iphone → Application → Navigation based Application. Name the project RssReader.

The first thing we need to do is to create a class that will download and parse a rss feed. For this application we will use the MdnShow podcast available at http://feeds2.feedburner.com/TheMdnShow, but you can use your own feed if you want.

Part 1: Parsing the rss feed

In this part we are going to download and parse the rss feed.

Create a new ObjectiveC class named Parser inside the Classes folder.

This class will have one method that accepts a url parameter and a delegate. Our navigation controller will call this method and set itself as the delegate. Once the parser has finished downloading and parsing the xml feed, it will send a message back to our navigation controller. This is how the process looks from a 10k feet view:

The code for Parser.h is the following:

#import <Foundation/Foundation.h>

@protocol ParserDelegate <NSObject>
- (void)receivedItems:(NSArray *)theItems;
@end

@interface Parser : NSObject <NSXMLParserDelegate> {
	id _delegate;
	
	NSMutableData *responseData;
	NSMutableArray *items;
	
	NSMutableDictionary *item;
	NSString *currentElement;
	NSMutableString * currentTitle, * currentDate, * currentSummary, * currentLink, * currentPodcastLink;
}

@property (retain, nonatomic) NSMutableData *responseData;
@property (retain, nonatomic) NSMutableArray *items;
@property (retain, nonatomic) NSMutableString *currentTitle;
@property (retain, nonatomic) NSMutableString *currentDate;
@property (retain, nonatomic) NSMutableString *currentSummary;
@property (retain, nonatomic) NSMutableString *currentLink;
@property (retain, nonatomic) NSMutableString *currentPodcastLink;

- (void)parseRssFeed:(NSString *)url withDelegate:(id)aDelegate;

- (id)delegate;
- (void)setDelegate:(id)new_delegate;

@end

Note that we are keeping a reference of our _delegate and declaring the method receivedItems: on the top. That method must be implemented in the delegate class, we’ll do this later. responseData contains the xml data and items contains our items list. The other instance variables are used while parsing the xml data.

The implementation file looks as follow:

#import "Parser.h"


@implementation Parser

@synthesize items, responseData;
@synthesize currentTitle;
@synthesize currentDate;
@synthesize currentSummary;
@synthesize currentLink;
@synthesize currentPodcastLink;

- (void)parseRssFeed:(NSString *)url withDelegate:(id)aDelegate {
	[self setDelegate:aDelegate];

	responseData = [[NSMutableData data] retain];
	NSURL *baseURL = [[NSURL URLWithString:url] retain];
	
	
	NSURLRequest *request = [NSURLRequest requestWithURL:baseURL];
	
	[[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSString * errorString = [NSString stringWithFormat:@"Unable to download xml data (Error code %i )", [error code]];
	
    UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:@"Error loading content" message:errorString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [errorAlert show];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
	self.items = [[NSMutableArray alloc] init];
	
	NSXMLParser *rssParser = [[NSXMLParser alloc] initWithData:responseData];
	
	[rssParser setDelegate:self];
	
	[rssParser parse];
}

#pragma mark rssParser methods

- (void)parserDidStartDocument:(NSXMLParser *)parser {
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
	currentElement = [elementName copy];
	
    if ([elementName isEqualToString:@"item"]) {
        item = [[NSMutableDictionary alloc] init];
        self.currentTitle = [[NSMutableString alloc] init];
        self.currentDate = [[NSMutableString alloc] init];
        self.currentSummary = [[NSMutableString alloc] init];
        self.currentLink = [[NSMutableString alloc] init];
		self.currentPodcastLink = [[NSMutableString alloc] init];
    }
	
	// podcast url is an attribute of the element enclosure
	if ([currentElement isEqualToString:@"enclosure"]) {
		[currentPodcastLink appendString:[attributeDict objectForKey:@"url"]];
	}
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
	
    if ([elementName isEqualToString:@"item"]) {
        [item setObject:self.currentTitle forKey:@"title"];
        [item setObject:self.currentLink forKey:@"link"];
        [item setObject:self.currentSummary forKey:@"summary"];
		[item setObject:self.currentPodcastLink forKey:@"podcastLink"];
		
		// Parse date here
		NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
		
		[dateFormatter setDateFormat:@"E, d LLL yyyy HH:mm:ss Z"]; // Thu, 18 Jun 2010 04:48:09 -0700
		NSDate *date = [dateFormatter dateFromString:self.currentDate];
		
        [item setObject:date forKey:@"date"];
		
        [items addObject:[item copy]];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    if ([currentElement isEqualToString:@"title"]) {
        [self.currentTitle appendString:string];
    } else if ([currentElement isEqualToString:@"link"]) {
        [self.currentLink appendString:string];
    } else if ([currentElement isEqualToString:@"description"]) {
        [self.currentSummary appendString:string];
    } else if ([currentElement isEqualToString:@"pubDate"]) {
		[self.currentDate appendString:string];
		NSCharacterSet* charsToTrim = [NSCharacterSet characterSetWithCharactersInString:@" \n"];
		[self.currentDate setString: [self.currentDate stringByTrimmingCharactersInSet: charsToTrim]];
    }
}

- (void)parserDidEndDocument:(NSXMLParser *)parser {
	if ([_delegate respondsToSelector:@selector(receivedItems:)])
        [_delegate receivedItems:items];
    else
    { 
        [NSException raise:NSInternalInconsistencyException
					format:@"Delegate doesn't respond to receivedItems:"];
    }
}

#pragma mark Delegate methods

- (id)delegate {
	return _delegate;
}

- (void)setDelegate:(id)new_delegate {
	_delegate = new_delegate;
}

- (void)dealloc {
	[items release];
	[responseData release];
	[super dealloc];
}
@end

This code might look complex but in reality it’s not hard to understand. In the first part we are downloading the xml data with NSURLConnection. When the data has been successfully downloaded, the connectionDidFinishLoading: method is called. At that point we use NSXMLParser to parse the data that we have already downloaded. In theory could tell NSXMLParser to download the data itself by passing a url, but this process would block the user interface for the time that the data is being downloaded and processed, instead we want to show a spinner while the user is waiting. The rest of the code involves parsing the xml data. When NSXMLParser has finished its job, parserDidEndDocument: is called and we send our items back to the delegate class.

Part 2: Displaying our items to the user

Now we can finally start our creative work. Back in our RootViewController, there are 4 things we need to do:

  1. Instantiate a spinner so that we can show and hide it when necessary.
  2. Send a request to the Parser class to load and parse the data.
  3. Implement the method receivedItems: that will be called by our Parser.
  4. Configure the table view.

The following is how RootViewController.h should look like:

  @interface RootViewController : UITableViewController {
  	UIActivityIndicatorView *activityIndicator;
  	NSArray *items;
  }

  @property (retain, nonatomic) UIActivityIndicatorView *activityIndicator;
  @property (retain, nonatomic) NSArray *items;

  @end

activityIndicator is a reference to our spinner, and items are the items returned by the Parser class. In the implementation file we need to import the Parser class, generate accessors methods and release our instance variables at the end:

  #import "RootViewController.h"
  #import "Parser.h"

  @interface RootViewController (PrivateMethods)
  - (void)loadData;
  @end

  @implementation RootViewController

  @synthesize activityIndicator, items;
  
  // Other code in the class
  
  - (void)dealloc {
  	[activityIndicator release];
  	[items release];
      [super dealloc];
  }

  @end

Note that we are declaring a private method loadData. We will use this method later. Now we’re ready to go through all the steps.

Step 1: Instantiate a spinner so that we can show and hide it when necessary

What we want to do is placing a spinner in the navigation bar in the top right. To do so we overwrite the viewDidLoad method:

  - (void)viewDidLoad {
    [super viewDidLoad];

    UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
  	indicator.hidesWhenStopped = YES;
  	[indicator stopAnimating];
  	self.activityIndicator = indicator;
  	[indicator release];

  	UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithCustomView:indicator];
  	self.navigationItem.rightBarButtonItem = rightButton;
  	[rightButton release];
  }

Our spinner is an instance of UIActivityIndicatorView. There are many indicators available, but we want the white one.

Step 2: Send a request to the Parser class to load and parse the data

At this point we need to tell to the Parser class to download and parse the rss feed. We can do so by overwriting the viewDidAppear method:

  - (void)viewDidAppear:(BOOL)animated {
  	[self loadData];
      [super viewDidAppear:animated];
  }

  - (void)loadData {
  	if (items == nil) {
  		[activityIndicator startAnimating];

  		Parser *rssParser = [[Parser alloc] init];
  		[rssParser parseRssFeed:@"http://feeds2.feedburner.com/TheMdnShow" withDelegate:self];

  		[rssParser release];

  	} else {
  		[self.tableView reloadData];
  	}

  }

We check to see if we already downloaded the items, and in that case we simply reload our table. If the items are nil, we show the spinner and call the parseRssFeed: method with the feed url.

Step 3: Implement the method receivedItems: that will be called by our Parser

When the Parser class is finished downloading and parsing the items, it will call the receivedItems: method:

  - (void)receivedItems:(NSArray *)theItems {
  	items = theItems;
  	[self.tableView reloadData];
  	[activityIndicator stopAnimating];
  }

Step 4: Configure the table view

At this point you can build and run the application, but the resulting table view would still be empty. We still have to tell our table view to use the items we downloaded. To do so, overwrite the following methods in RootViewController.m:

  // Customize the number of rows in the table view.
  - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      return [items count];
  }


  // Customize the appearance of table view cells.
  - (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];
      }

  	// Configure the cell.

  	cell.textLabel.text = [[items objectAtIndex:indexPath.row] objectForKey:@"title"];

  	// Format date
  	NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];	
  	[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
  	[dateFormatter setTimeStyle:NSDateFormatterNoStyle];

  	cell.detailTextLabel.text = [dateFormatter stringFromDate:[[items objectAtIndex:indexPath.row] objectForKey:@"date"]];
  	cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

      return cell;
  }

In tableView:numberOfRowsInSection: we simply return the number of items available. In tableView:cellForRowAtIndexPath: we set up the cell to show the title and the date of the item. If you build and run the application now, this is what you would see:

Unfortunately, if you click on an item nothing happens. We will fix this in the next part.

Part 3: Showing item details

When we click a row, we want to show a new window with all the feed details. We have to create a new controller called DetailController, along with a xib file that it will be our interface.

Select File → New file and select UIViewController subclass. Make sure that with XIB for user interface is checked. Name the class DetailController.

At this point I rename DetailController.xib to Detail.xib and I move it to the Resources folder.

Double click Detail.xib to open interface builder. Drag two text labels and an instance of UIWebView, and a button that we will use to start the podcast:

Now we need to declare some instance variables and one action in our DetailController. This is how DetailController.h should look like:

  #import <UIKit/UIKit.h>

  @interface DetailController : UIViewController {
  	NSDictionary *item;
  	IBOutlet UILabel *itemTitle;
  	IBOutlet UILabel *itemDate;
  	IBOutlet UIWebView *itemSummary;
  }

  @property (retain, nonatomic) NSDictionary *item;
  @property (retain, nonatomic) IBOutlet UILabel *itemTitle;
  @property (retain, nonatomic) IBOutlet UILabel *itemDate;
  @property (retain, nonatomic) IBOutlet UIWebView *itemSummary;

  - (id)initWithItem:(NSDictionary *)theItem;

  - (IBAction)playPodcast:(id)sender;

  @end
</UIKit>

Don't worry about <em>initWithItem:</em> for now. You may be wondering why we used a <em>UIWebView</em> for the summary of the item. The reason is that we want to display the text as html, and using <em>UIWebView</em> is perfect for that.

The implementation file looks as follow:

<pre name="code" class="objc">
  #import "DetailController.h"


  @implementation DetailController

  @synthesize item, itemTitle, itemDate, itemSummary;
  
  // other code
  
  - (void)dealloc {
  	[item release];
  	[itemTitle release];
  	[itemDate release];
  	[itemSummary release];
      [super dealloc];
  }


  @end

Now we can go back to interface builder (save first) and connect our labels to our instance variables, and the play button to our IBAction.

Back in xcode, it’s time to finish our application. Our RootViewController class will call a initWithItem: method in our DetailController class. This method will setup the title of the window and assign the instance of item. We also overwrite viewDidLoad to update the labels value and add a method to play the podcast. Add this code to DetailController.m:

  - (id)initWithItem:(NSDictionary *)theItem {
  	if (self = [super initWithNibName:@"Detail" bundle:nil]) {
  		self.item = theItem;
  		self.title = [item objectForKey:@"title"];
  	}

  	return self;
  }

  /*
   // The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
  - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
      if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
          // Custom initialization
      }
      return self;
  }
  */

  - (void)viewDidLoad {
      [super viewDidLoad];

  	self.itemTitle.text = [item objectForKey:@"title"];

  	NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];	
  	[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
  	[dateFormatter setTimeStyle:NSDateFormatterNoStyle];

  	self.itemDate.text = [dateFormatter stringFromDate:[item objectForKey:@"date"]];

  	[self.itemSummary loadHTMLString:[item objectForKey:@"summary"] baseURL:nil];
  }

  - (IBAction)playPodcast:(id)sender {
  	NSURLRequest *request = [[NSURLRequest alloc]
  							 initWithURL: [NSURL URLWithString: [item objectForKey:@"podcastLink"]]]; 

  	[self.itemSummary loadRequest:request];
  	[request release];
  }

There’s still one thing we need to do. We haven’t told yet to our table view what to do when a user clicks on a row. To fix that, overwrite the tableView:didSelectRowAtIndexPath: method in RootViewController.m:

  - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 	 NSDictionary *theItem = [items objectAtIndex:indexPath.row];
 	 DetailController *nextController = [[DetailController alloc] initWithItem:theItem];
 	 [self.navigationController pushViewController:nextController animated:YES];
 	 [nextController release];
  }

Remember to add the import statement for DetailController at the top of RootViewController.m. Build and run the application, you should now be able to play around and listen to podcasts.

Conclusion

Creating this application was a lot of work, but hopefully you’ve been able to follow the process. Feel free to download the source code and to study it. Also check the documentation if you want to know more about the classes we used. Finally feel free to leave a comment if something is unclear.

Download application source code

You should subscribe to the RSS Feed Here.

Posted on 18 Jun 2010

Comments

  1. Avatarmemo Says:
    June 18 2010 at 15:45

    Nice tutorial!
    Especially to noobs like me.

  2. June 18 2010 at 16:12

    Great tutorial. Would love to find more resources like these and maybe ones that take it one level deeper and explain syntax and coding choices.

    Jarrod

  3. AvatarMike Says:
    June 18 2010 at 21:02

    Where does this code belong? It is not very clear. I thought in the delegate but items is defined in Parser.

    - (void)receivedItems:(NSArray *)theItems { items = theItems; [self.tableView reloadData]; [activityIndicator stopAnimating]; }
  4. AvatarMike Says:
    June 18 2010 at 21:11

    I downloaded the source. It’s in the RootViewController.m

    - (void)receivedItems:(NSArray *)theItems { …}

    Is there an easy way to specify in which file each snippet should be placed?

  5. AvatarJordan Says:
    June 18 2010 at 21:58

    Great job on the tutorial! Good Work!

  6. AvatarOscar Says:
    June 18 2010 at 23:21

    Hey Mike, sorry about that, but glad you figured it out.

  7. Avatarkk Says:
    June 26 2010 at 19:02

    Downloaded the code. Application gets installed on simulator but in 2-3 seconds it closes. Any idea ?

  8. AvatarJane Says:
    June 28 2010 at 08:27

    “Downloaded the code. Application gets installed on simulator but in 2-3 seconds it closes.”

    This happened to me too. I use iOS4 SDK xcode 3.2.3

  9. AvatarJose Says:
    July 07 2010 at 17:02

    Same problem as @kk and @Jane,

    is there any way to fix this? thanks!

  10. AvatarJose Says:
    July 07 2010 at 17:02

    Same problem as @kk and @Jane,

    is there any way to fix this? thanks!

  11. AvatarLaurent Says:
    July 07 2010 at 22:11

    bump, same problem with ios 4 sdk

    however, great tutorial, thanks

  12. AvatarKatie Says:
    July 13 2010 at 06:32

    Same here on the stalling/closing. Would love a fix, thanks for the wonderful tutorial!

  13. AvatarOscar Says:
    July 13 2010 at 10:12

    Hey Guys, I’m currently on a 3 days trip. I’ll look into it as soon as I get home.

  14. AvatarMartijn Says:
    July 14 2010 at 00:45

    Thanks for the tutorial.
    I get the warning: “class ‘Parser’ does not implement the ‘NSXMLParserDelegate’ protocol”

    Unfortunately it does not work

  15. AvatarOscar Says:
    July 16 2010 at 09:16

    Hi guys, the project was developed with the 3.1.3 sdk. I’m going to upgrade to the latest sdk asap and I’ll email each of you once I fix this.

  16. July 16 2010 at 13:55

    Thanks for the tutorial, I was able to get it up and running perfectly on iOS4.

    There were a few issues with the code as-is. The NSMLParserDelegate wasn’t being declared in the Parser class, there were null pointer exceptions due to the current* NSMutableStrings being out of scope and the date was being parsed wrong.

    The first was easy to fix, I just declared the NSXML delegate in Parser.h:

    @interface Parser : NSObject {

    I’m not sure about the technical details of the second, I thought it should work but the debugger said that the current* variables were “out of scope”. In any case I had seen this before and was able to work around it by creating properties for all of them and using them with the “dot” notation, like self.currentDate:

    // Add properties in Parser.h
    @property (retain, nonatomic) NSMutableString *currentTitle;
    @property (retain, nonatomic) NSMutableString *currentDate;
    @property (retain, nonatomic) NSMutableString *currentSummary;
    @property (retain, nonatomic) NSMutableString *currentLink;
    @property (retain, nonatomic) NSMutableString *currentPodcastLink;

    // Synthesize them in Parser.m
    Then synthesizing them like this:
    @synthesize currentTitle;
    @synthesize currentDate;
    @synthesize currentSummary;
    @synthesize currentLink;
    @synthesize currentPodcastLink;

    // Look for all occurrences of these and replace with self.currentWhatever.

    Finally, for some reason the date field had a lot of newlines and spaces after it, I trimmed in the foundCharacters handler with:

    } else if ([currentElement isEqualToString:@"pubDate"]) { [self.currentDate appendString:string]; NSCharacterSet* charsToTrim = [NSCharacterSet characterSetWithCharactersInString:@" \n"]; [self.currentDate setString: [self.currentDate stringByTrimmingCharactersInSet: charsToTrim]]; }
  17. AvatarWill Says:
    July 16 2010 at 17:12

    For the first issue I meant:

    @interface Parser : NSObject {

  18. AvatarOscar Says:
    July 17 2010 at 09:48

    @Will, thank you. I have updated the code and now it’s working again!

  19. AvatarZakhar Says:
    July 23 2010 at 16:58

    @Will
    What is this?
    The first was easy to fix, I just declared the NSXML delegate in Parser.h:

    @interface Parser : NSObject {

    For the first issue I meant:

    @interface Parser : NSObject {

    Why are you writing this? what was wrong with it and what is the difference?

  20. AvatarWill Says:
    July 23 2010 at 22:02

    I tried posting angle brackets but they didn’t show up, I thought I made a mistake so tried posting it again.

  21. AvatarRazvan Aurariu Says:
    July 25 2010 at 09:41

    Hi,

    I noticed that the back button from the DetailView NavigationBar is missing. You could add that in by writing this code into your -(void)viewDidLoad method in RootViewCotroller.m :

    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered target:nil action:nil]; self.navigationItem.backBarButtonItem = backButton; [backButton release];

    Cheers,
    Razvan.

  22. AvatarRazvan Aurariu Says:
    July 25 2010 at 09:44

    And there is already a charset for trimming whitespaces and newline characters, it’s called [NSCharacterSet whitespaceAndNewlineCharacterSet]. Sry for double posting :)

  23. AvatarMightyMike Says:
    July 25 2010 at 21:10

    Is there a way to parse the image data from the url and use it in the tableview as well?

    Thanks

  24. Avatarbeev Says:
    July 26 2010 at 14:49

    Thanks for this tutorial. Very interesting.

    I downloaded the source code but had to remove a few characters to get it to run. I changed this line in Parser.h:
    @interface Parser : NSObject {

    to this:
    @interface Parser : NSObject {

  25. Avatarbeev Says:
    July 26 2010 at 14:50

    …. something isn’t right there!!

  26. Avatarbeev Says:
    July 26 2010 at 15:14

    So it’s not displaying text between angle brackets. After NSObject in the first line it should say “NSURLParserDelegate”, and at the same place in the second line it should just say “ParserDelegate”.

    I’m finding that when I replace the feed address (http://feeds2.feedburner.com/TheMdnShow) with a different one (feed://scottishpoetrylibrary.podomatic.com/rss2.xml) it won’t work. Simulator shows error code 1002 (Unable to download xml data). The second address is from a working feed. I’m not clear on what the difference is. I mean, I can see the obvious differences in the addresses, but I can’t tell why that would prevent it from loading.

  27. Avatarbeev Says:
    July 26 2010 at 22:02

    Ok, I am being dim. It’s because it actually works with Atom feeds, not RSS.

    Apologies for four comments in a row…

  28. AvatarAli W Says:
    July 27 2010 at 21:32

    This is a very good tutorial. I’m not very familiar with RSS feeds but I would like to post the entire article instead of the summary/description.
    I can find the element name for the entire story.
    Found several prospects: article, body, content. Nothing is quite working and I would like to know exactly what to use before I alter too many things.
    Thanks,

  29. AvatarAli W Says:
    July 27 2010 at 22:41

    I of course meant to say that I “cannot” find, the element name for the entire article

  30. AvatarAli W Says:
    July 28 2010 at 02:38

    Upon further investigation I have no answer to my earlier question, although I have eliminated a lot of suspects.

    My question, to state more clearly now is, how can i get the detail view to display an entire article, rather than the abbreviated version?

    Thank you in advance,

  31. AvatarAli W Says:
    July 28 2010 at 16:18

    Why is the amount of the text from the rss feed that is displayed so limited. I want to be able to expand the amount of text displayed.
    Can anyone tell me how to do this?

    I’m new to iphone dev, and on crushing deadline.

    Thank you so much,

  32. AvatarAli W Says:
    July 28 2010 at 16:47

    Sorry for the bother of my messages. The issue was changing from the element “description” to the element “content:encoded”.

    Very well done tutorial, BTW.

  33. AvatarAli W Says:
    July 28 2010 at 17:01

    Can someone tell me how to change the font in the UIWebView to Helvetica?

    Thank you,

  34. Avataranti Says:
    July 29 2010 at 11:56

    Ali W have you managed to expand the amount of text displayed from the rss feed? I have a problem in the table, no matter the font size, the last 2 or 3 letters are changed for “…”
    Any idea?

  35. AvatarBonglah Says:
    July 29 2010 at 12:21

    Nice tutorial!
    I’ve just started with xCode.
    It runs perfectly.

  36. AvatarBonglah Says:
    July 29 2010 at 12:24

    Can you update it other tasks such as star, share, unread…

    Thank you.

  37. AvatarDangerWillRobinson Says:
    July 29 2010 at 20:21

    How can you push the xml data to a detail view without using tableview?

  38. AvatarLMason Says:
    July 29 2010 at 20:58

    So When I run the app I get the titles but no date in the cells. Is this happening to anyone else? Why is this?

  39. AvatarLMason Says:
    July 29 2010 at 20:59

    So When I run the app I get the titles but no date in the cells. Is this happening to anyone else? Why is this?

  40. AvatarOscar Says:
    July 30 2010 at 07:45

    @LMason, are you using the same feed for your test?

  41. AvatarAli W Says:
    August 03 2010 at 22:47

    To Anti,

    I changed the lines for description in Parser.m to this:
    } else if ([currentElement isEqualToString:@"content:encoded"]) {
    [self.currentSummary appendString:string];

  42. AvatarAli W Says:
    August 04 2010 at 16:39

    Sorry Anti,

    I was rushing yesterday. I used the code above to replace the code for “description”. Sorry to not be more clear. (upgrading to 4.0 not going to well.)

  43. AvatarAli W Says:
    August 04 2010 at 16:39

    Sorry Anti,

    I was rushing yesterday. I used the code above to replace the code for “description”. Sorry to not be more clear. (upgrading to 4.0 not going to well.)

  44. AvatarAli W Says:
    August 04 2010 at 16:47

    After upgrading my sdk to 4.0 my rss reader no longer is working.

    Actually, it works with the MDN blog that is included in the tutorial, but it doesn’t work with the blog that I want to display, and which worked previously.

    The RootViewController loads the title and the dividing lines of the tableView appear, but then it totally crashes.

    The error I get is:

    0×90c4eef6 <0010> jae 0×90c4ef06 <__kill26>

    • Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ’-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: date)

    I blocked out this line in Parser.m and it will load on the simulator, albeit without the date (which needs to be there):

    [item setObject:date forKey:@"date"]

    I thought there must be some difference between the rss feeds which work, and don’t work. I pulled source code from both however and cannot see a difference.

    Source code in blog I want to use:
    …. says, “Tell the truth, and shame the devil!”

    Apr 27, 3:45 PM

Ooops! That might have been you. I

source code from mdn blog:


…idget Press and Macoscope

Jun 4, 1:37 PM

While Jiva DeVoe talks about Push Noti….

I’m at a loss. Has anyone else encountered this problem and discovered the solution?

Can anyone see what I’m missing?

Thank you for any help you have,

  • AvatarAli W Says:
    August 04 2010 at 22:08

    I have updated to sdk 4.0 on my machine. One rss feed I have is working, and the other is crashing, but both have this error:

    [rssParser setDelegate:self]; (Class Parser does not implement NSXMLParser Delegate) although I believe have followed all the changes:
    @protocol ParserDelegate
    - (void)receivedItems:(NSArray *)theItems;
    @end
    @protocol NSXMLParserDelegate;

    @interface Parser : NSObject{
    id _delegate;
    NSMutableData *responseData;

  • AvatarAli W Says:
    August 05 2010 at 21:03

    Just call me desperate.

    If I take this code out of the Parser.m

    either rss will run, albeit without dates.

    // Parse date here
    NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];

    [dateFormatter setDateFormat:@"E, d LLL yyyy HH:mm:ss Z"]; // Thu, 18 Jun 2010 04:48:09 -0700

    NSDate *date = [dateFormatter dateFromString:self.currentDate];

    [item setObject:date forKey:@"date"];

    Until I upgraded to sdk 4.0, either of my feeds would run.
    The feed that is not working is rss.
    Any help, greatly appreciated.

  • AvatarAli W Says:
    August 05 2010 at 23:22

    I have one rss feed (MDN) which is working, and one Atom feed which is not working, crashing the date line: [item setObject:date forKey:@’date"]

    Anyone know how to convert the date parse so it will work. The dates in both feeds look identical to me.

  • AvatarAli W Says:
    August 05 2010 at 23:27

    I have one rss feed (not working) and the MDN feed which I believe is ATOM and it is working.

    The rss feed crashes on the line:

    [item setObject:date forKey:@"date"];

    Does anyone know how to make this work in SDK 4, with rss?

  • AvatarBarbara M Says:
    August 06 2010 at 09:46

    I am having the same problem mentioned above, the simulater opens but after a few seconds it crashes. The only change I have made is to replace the feed URL with my own. I am running SDK 4.0.

  • AvatarAli W Says:
    August 06 2010 at 16:07

    To add to the above issue, I have compared the source code from both feeds, the one which works, and the one which doesn’t, and the pubDate lines are identical.

    And it worked in 3.1.

  • AvatarPatrick Says:
    August 10 2010 at 04:11

    Hi,

    I downloaded the source code and it doesn’t working …

    Im using XCode 3.2.3 with simulator 4.0 …
    Can you update the .zip please?

    Thanks

  • AvatarTimT Says:
    August 13 2010 at 18:54

    Is there a way to pull the data from node attributes instead of just the node values? I.E.

  • AvatarSkato Says:
    August 25 2010 at 17:56

    tried to use this as a base, but the dateformatter code is not working. Some help would be appriciated, as i am not sure what to do…

  • Avatartesting Says:
    August 26 2010 at 18:07

    Why do i retain here?

    responseData = [[NSMutableData data] retain];

    If I don’t retain I get a NSXMLParserPrematureDocumentEndError (parseError code = 5)

    I have to note that I implemented the parseErrorOccurred method.

  • August 28 2010 at 21:51

    Everyone having issues with using an alternative RSS feed… it is not trimming the date well enough, hence the NSDateFormatter not working properly.

    Simply use:

    NSDate *thedate = [dateFormatter dateFromString:[self.currentDate stringByTrimmingCharactersInSet:
    [NSCharacterSet whitespaceAndNewlineCharacterSet]]];

    FIXED!

  • Avatartesting Says:
    August 30 2010 at 12:54

    Don’t use copy here! It is only generating memory leaks! So take each line with a copy in it and delete the copy. I don’t understand why he is doing it. The retain count is 2 and you cannot release it, because you don’t have a pointer to it.

  • Avatarpelin soykan Says:
    September 03 2010 at 14:30

    I’m sorry if this has been mentioned in older comments but I’ve found out that the part in viewDidLoad regarding activity indicator should be the following or the code will not work:

    activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
    activityIndicator.hidesWhenStopped = YES;
    [activityIndicator stopAnimating];

    UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator]; self.navigationItem.rightBarButtonItem = rightButton; [rightButton release];

    [activityIndicator release];