Gold Challenge Solution


#1

Here’s my solution to the gold challenge:

RSSItem.h

[code]@property (nonatomic, weak) id parentPostItem;
@property (nonatomic, getter = isParent) BOOL parent;
@property (nonatomic) BOOL childrenVisible;
@property (nonatomic) int childrenCount;

  • (BOOL)isChild;
    [/code]parentPostItem is a pointer to its parent
    isParent flag is used to set cell accessory
    childrenVisible flag is to track whether to show/hide child posts
    childrenCount is used when animating the show/hide
    isChild. since I’ve already got isParent

RSSItem.m

@synthesize parentPostItem, parent;
- (BOOL)isChild // gold challenge ch26
{
    if ([self parentPostItem]) {    // has pointer to parent
        return YES;
    } else {
        return NO;
    }
}

RSSChannel.h

[code]@property (nonatomic, readonly, strong) NSMutableArray *hiddenItems;

  • (void)showChildPosts:(RSSItem *)parent;
  • (void)hideChildPosts:(RSSItem *)parent;
  • (void)collapseAllChildPosts;
    [/code]hiddenItems holds the child posts that are hidden

RSSChannel.m > synthesize and init

[code]@synthesize hiddenItems; // gold challenge ch26

  • (id)init
    {
    self = [super init];
    if (self) {
    items = [[NSMutableArray alloc] init];
    hiddenItems = [[NSMutableArray alloc] init]; // gold challenge ch26
    }
    return self;
    }
    [/code]

RSSChannel.m > parser:didEndElement

[code]- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
currentString = nil;

if ([elementName isEqual:@"channel"]) {
    [parser setDelegate:parentParserDelegate];
    [self trimItemTitles];
    [self collapseAllChildPosts]; // gold challenge ch26
}

}
[/code]

RSSChannel.m > trimItemTitles

[code]- (void)trimItemTitles
{
// regex: Author
// updated for bronze challenge ch26
// updated again for silver challenge 26: reply
NSRegularExpression reg = [[NSRegularExpression alloc] initWithPattern:@"(.) :: (?:Re: )?(.) :: ."
options:0
error:nil];
NSRegularExpression shortReg = [[NSRegularExpression alloc] initWithPattern:@"(.) :: (?:Re: )?(.*)"
options:0
error:nil];
for (RSSItem *i in items) {
NSString *itemTitle = [i title];
NSArray *matches = [reg matchesInString:itemTitle
options:0
range:NSMakeRange(0, [itemTitle length])];
// find matches using truncated title
if ([matches count] == 0) {
matches = [shortReg matchesInString:itemTitle
options:0
range:NSMakeRange(0, [itemTitle length])];
}
if ([matches count] > 0) {
NSTextCheckingResult *result = [matches objectAtIndex:0];

        // bronze challenge ch26 begin
        if ([result numberOfRanges] == 3) {
            [i setSubforum:[itemTitle substringWithRange:[result rangeAtIndex:1]]];
            [i setTitle:[itemTitle substringWithRange:[result rangeAtIndex:2]]];
        }
        // bronze challenge ch26 end
        
        // gold challenge ch26 begin
        int currentIndex = [items indexOfObject:i];
        if (currentIndex > 0) {                 // skip the first object
            
            // previous item
            RSSItem *pi = [items objectAtIndex:currentIndex - 1];
            
            if ([[i title] isEqual:[pi title]]) {  // we found a child post
                if ([pi isChild]) {             // the previous item is another child
                    [i setParentPostItem:[pi parentPostItem]];
                } else {                        // the previous item is parent
                    [i setParentPostItem:pi];
                    [pi setParent:YES];         // set parent flag
                    [pi setChildrenVisible:YES];
                }
            }
        }
        // gold challenge ch26 end
    }
}

}
[/code]

RSSChannel.m > show/hide child posts

[code]- (void)showChildPosts:(RSSItem *)parent // gold challenge ch26
{
GoldLog(@“incoming parent:%@”, parent);

// get index to insert child posts
int insertIndex = [items indexOfObject:parent] + 1;

GoldLog(@"checking hidden array (insert index set to:%d)", insertIndex);
[parent setChildrenCount:0];
for (int i = 0; i < [hiddenItems count];) {  // do not increment
    RSSItem *item = [hiddenItems objectAtIndex:i];
    GoldLog(@"checking: %@", item);
    if ([item parentPostItem] == parent) {
        GoldLog(@"\tfound a child! toggle to SHOW");
        [items insertObject:item atIndex:insertIndex];
        [hiddenItems removeObject:item];
        [parent setChildrenCount:[parent childrenCount] + 1];
        insertIndex++;
        GoldLog(@"\tinsert index is:%d", insertIndex);
    } else {
        GoldLog(@"not a child. next...");
        i++; // increment if not a child
    }
}
GoldLog(@"finished");
[parent setChildrenVisible:YES];

}

  • (void)hideChildPosts:(RSSItem *)parent // gold challenge ch26
    {
    GoldLog(@“incoming parent:%@”, parent);

    // get first child post index
    int childIndex = [items indexOfObject:parent] + 1;

    GoldLog(@“checking items array starting at: %d”, childIndex);
    [parent setChildrenCount:0];
    for (int i = childIndex; i < [items count]:wink: { // do not increment
    RSSItem *child = [items objectAtIndex:i];
    GoldLog(@“checking: %@”, child);
    if ([child parentPostItem] == parent) {
    GoldLog(@"\tfound a child! toggle to HIDE");
    [hiddenItems addObject:child];
    [items removeObject:child];
    [parent setChildrenCount:[parent childrenCount] + 1];
    } else {
    GoldLog(@“no child. end search!”);
    break; // stop searching
    }
    }
    GoldLog(@“finished”);
    [parent setChildrenVisible:NO];
    }

  • (void)collapseAllChildPosts // gold challenge ch26
    {
    NSMutableArray *parents = [[NSMutableArray alloc] init];
    for (RSSItem *i in items) {
    GoldLog(@“building parents array… checking: %@”, i);
    if ([i isParent]) { // is a parent
    GoldLog(@"\tis a parent!");
    [parents addObject:i];
    }
    }
    for (RSSItem *i in parents) {
    GoldLog(@“collapsing parent: %@”, i);
    [self hideChildPosts:i];
    }
    }
    [/code]GoldLog = NSLog

ListViewController.m > cellForRowAtIndexPath

[code]- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@“UITableViewCell”];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:@“UITableViewCell”];
}
RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];

[[cell textLabel] setText:[item title]];
[[cell detailTextLabel] setText:[item subforum]]; // bronze challenge ch26

// gold challenge ch26 begin
if ([item isParent]) {
    [cell setAccessoryType:UITableViewCellAccessoryDetailButton];
} else {
    [cell setAccessoryType:UITableViewCellAccessoryNone];
}
if ([item isChild]) {
    [cell setIndentationLevel:1];
} else {
    [cell setIndentationLevel:0];
}
// gold challenge ch26 end

return cell;

}
[/code]

ListViewController.m > accessoryButtonTappedForRowWithIndexPath

[code]- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath // gold challenge ch26
{
int row = [indexPath row];
RSSItem *item = [[channel items] objectAtIndex:row];

// build array of indexpaths
NSMutableArray *ips = [[NSMutableArray alloc] init];
for (int i = 0; i < [item childrenCount]; i++) {
    NSIndexPath *ip = [NSIndexPath indexPathForRow:++row inSection:0];
    [ips addObject:ip];
}

if ([item childrenVisible]) {
    [channel hideChildPosts:item];
    [[self tableView] deleteRowsAtIndexPaths:ips withRowAnimation:UITableViewRowAnimationTop];
} else {
    [channel showChildPosts:item];
    [[self tableView] insertRowsAtIndexPaths:ips withRowAnimation:UITableViewRowAnimationTop];
}

}
[/code]

I’ll try to go back and use a Thread class and maybe clean up this code. Please comment.


#2

If you are using a Thread for concurrency
To add concurrency to your app, use a Grand Central Dispatch queue and to it submit blocks for execution.


#3

I meant RSSThread (sorry about that) since most of the other gold solutions used a new class. Here’s my attempt:

RSSThread.h

[code]#import <Foundation/Foundation.h>

@class RSSItem;

@interface RSSThread : NSObject

// the first object in the thread is considered the parent
// there can be any number of child posts, including zero
@property (nonatomic, strong) NSMutableArray *thread;
@property (nonatomic) BOOL showChildren;

  • (id)initWithPost:(RSSItem *)post;
  • (void)addPost:(RSSItem *)post;

@end
[/code]

RSSThread.m

[code]#import “RSSThread.h”

@implementation RSSThread

@synthesize thread, showChildren;

  • (id)init
    {
    self = [super init];
    if (self) {
    GoldLog(@“thread.init”);
    thread = [[NSMutableArray alloc] init];
    showChildren = NO;
    }
    return self;
    }
  • (id)initWithPost:(RSSItem *)post
    {
    self = [self init];
    if (self) {
    GoldLog(@“thread.initWithPost:%@”, post);
    [self addPost:post];
    }
    return self;
    }
  • (void)addPost:(RSSItem *)post
    {
    GoldLog(@“thread.addPost:%@”, post);
    [thread addObject:post];
    }

@end
[/code]

I needed a class to hold all the threads so I created a store.

RSSThreadStore.h

[code]#import <Foundation/Foundation.h>

@class RSSThread, RSSChannel, RSSItem;

@interface RSSThreadStore : NSObject
{
NSMutableArray *allThreads;
}

  • (RSSThreadStore *)sharedStore;
  • (void)importChannel:(RSSChannel *)channel;
  • (int)numberOfRows; // for tableview
  • (id)objectAtIndex:(int)index objectType:(int)type;
  • (RSSItem *)itemAtIndex:(int)index;
  • (RSSThread *)threadAtIndex:(int)index;

@end
[/code]

RSSThreadStore.m

[code]#import “RSSThreadStore.h”
#import “RSSThread.h”
#import “RSSChannel.h”
#import “RSSItem.h”

@implementation RSSThreadStore

  • (RSSThreadStore *)sharedStore
    {
    static RSSThreadStore *sharedStore = nil;
    if(!sharedStore)
    sharedStore = [[super allocWithZone:nil] init];
    return sharedStore;
    }
  • (id)allocWithZone:(NSZone *)zone
    {
    return [self sharedStore];
    }
  • (id)init
    {
    self = [super init];
    if (self) {
    allThreads = [[NSMutableArray alloc] init];
    }
    return self;
    }

  • (void)importChannel:(RSSChannel *)channel
    {
    GoldLog(@“threadstore.import_channel:%@”, channel);
    for (RSSItem *item in [channel items]) {
    GoldLog(@“threadstore.import_item:%@”, item);
    if ([item isChild]) { // child
    GoldLog(@“threadstore.import_item_is_child”);
    [[allThreads lastObject] addPost:item];
    } else { // parent or first object
    GoldLog(@“threadstore.import_item_is_parent”);
    [allThreads addObject:[[RSSThread alloc] initWithPost:item]];
    }
    }
    }

  • (int)numberOfRows
    {
    int i = 0;
    for (RSSThread *t in allThreads) {
    if ([t showChildren]) {
    i += [[t thread] count];
    } else {
    i++;
    }
    }
    GoldLog(@“threadstore.numberofrows:%d”, i);
    return i;
    }

  • (id)objectAtIndex:(int)index objectType:(int)type // type = 0 for RSSItem, 1 for RSSThread
    {
    int i = 0; // beginning index
    int j = 0; // ending index

    for (RSSThread *t in allThreads) {

      // set ending index
      if ([t showChildren]) {
          j = i + [[t thread] count] - 1;
      } else {
          j = i;
      }
      
      // search between beginning and ending indexes
      if (i <= index && j >= index) {
          if (type == 0) {
              GoldLog(@"threadstore.objectatindex:(%d=[%d,%d])%@", index, i, j, [[t thread] objectAtIndex:index - i]);
              return [[t thread] objectAtIndex:index - i];
          } else {
              GoldLog(@"threadstore.objectatindex:(%d=[%d,%d])%@", index, i, j, t);
              return t;
          }
      }
      
      // increment
      i = j + 1;
    

    }
    return nil;
    }

  • (RSSItem *)itemAtIndex:(int)index
    {
    return [self objectAtIndex:index objectType:0];
    }

  • (RSSThread *)threadAtIndex:(int)index
    {
    return [self objectAtIndex:index objectType:1];
    }

@end
[/code]Edited: importChannel:

ListViewController.m

[code]#import “ListViewController.h”
#import “RSSChannel.h”
#import “RSSItem.h”
#import “WebViewController.h”
#import “ChannelViewController.h”
#import “RSSThreadStore.h” // gold challenge ch26
#import “RSSThread.h” // gold challenge ch26

@implementation ListViewController

@synthesize webViewController;

  • (id)initWithStyle:(UITableViewStyle)style
    {
    self = [super initWithStyle:style];
    if (self) {
    UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithTitle:@“Info” style:UIBarButtonItemStyleBordered target:self action:@selector(showInfo:)];
    [[self navigationItem] setRightBarButtonItem:bbi];

      [self fetchEntries];
    

    }
    return self;
    }

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
    return [[RSSThreadStore sharedStore] numberOfRows]; // gold challenge ch26
    //return [[channel items] count];
    }

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@“UITableViewCell”];
    if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
    reuseIdentifier:@“UITableViewCell”];
    }
    RSSItem *item = [[RSSThreadStore sharedStore] itemAtIndex:[indexPath row]]; // gold challenge ch26
    //RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];

    [[cell textLabel] setText:[item title]];
    [[cell detailTextLabel] setText:[item subforum]]; // bronze challenge ch26

    // gold challenge ch26 begin
    if ([item isParent]) {
    [cell setAccessoryType:UITableViewCellAccessoryDetailButton];
    } else {
    [cell setAccessoryType:UITableViewCellAccessoryNone];
    }
    if ([item isChild]) {
    [cell setIndentationLevel:1];
    } else {
    [cell setIndentationLevel:0];
    }
    // gold challenge ch26 end

    return cell;
    }

  • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    if (![self splitViewController]) {
    [[self navigationController] pushViewController:webViewController animated:YES];
    } else {
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:webViewController];
    NSArray *vcs = [NSArray arrayWithObjects:[self navigationController], nav, nil];

      [[self splitViewController] setViewControllers:vcs];
      [[self splitViewController] setDelegate:webViewController];
    

    }

    RSSItem *entry = [[RSSThreadStore sharedStore] itemAtIndex:[indexPath row]]; // gold challenge ch26
    //RSSItem *entry = [[channel items] objectAtIndex:[indexPath row]];
    [webViewController listViewController:self handleObject:entry];
    }

  • (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath // gold challenge ch26
    {
    int row = [indexPath row];
    RSSThread *t = [[RSSThreadStore sharedStore] threadAtIndex:row];

    // build array of indexpaths
    NSMutableArray *ips = [[NSMutableArray alloc] init];
    for (int i = 1; i < [[t thread] count]; i++) {
    NSIndexPath *ip = [NSIndexPath indexPathForRow:++row inSection:0];
    [ips addObject:ip];
    }

    if ([t showChildren]) {
    [t setShowChildren:NO];
    [[self tableView] deleteRowsAtIndexPaths:ips withRowAnimation:UITableViewRowAnimationTop];
    } else {
    [t setShowChildren:YES];
    [[self tableView] insertRowsAtIndexPaths:ips withRowAnimation:UITableViewRowAnimationTop];
    }
    }

  • (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
    {
    [xmlData appendData:data];
    }

  • (void)connectionDidFinishLoading:(NSURLConnection *)conn
    {
    NSString *xmlCheck = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding];
    WSLog(@“xmlCheck = %@”, xmlCheck);

    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
    [parser setDelegate:self];
    [parser parse];

    xmlData = nil;
    connection = nil;

    [[RSSThreadStore sharedStore] importChannel:channel]; // gold challenge ch26

    [[self tableView] reloadData];
    WSLog(@"%@\n %@\n %@\n", channel, [channel title], [channel infoString]);
    }

  • (void)connection:(NSURLConnection *)conn didFailWithError:(NSError *)error
    {
    connection = nil;
    xmlData = nil;

    NSString *errorString = [NSString stringWithFormat:@“Fetch failed: %@”, [error localizedDescription]];
    UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error"
    message:errorString
    delegate:nil
    cancelButtonTitle:@"OK"
    otherButtonTitles:nil];
    [av show];
    }

  • (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
    {
    WSLog(@"%@ found a %@ element", self, elementName);
    if ([elementName isEqual:@“channel”]) {
    channel = [[RSSChannel alloc] init];
    [channel setParentParserDelegate:self];
    [parser setDelegate:channel];
    }
    }

  • (void)fetchEntries
    {
    // create a new data container for the stuff that comes back from the service
    xmlData = [[NSMutableData alloc] init];

    // construct a url that will ask the service for what you want -
    // note we can concatenate literal strings together on multiple lines in this way -
    // this results in a single NSString instance
    NSURL *url = [NSURL URLWithString:
    @"http://forums.bignerdranch.com/smartfeed.php?"
    @“limit=7_DAY&sort_by=standard&feed_type=RSS2.0&feed_style=COMPACT”];

    // for apple’s hot news feed, replace the line above with
    //NSURL *url = [NSURL URLWithString:@“http://www.apple.com/pr/feeds/pr.rss”];

    // put that url into an nsurlrequest
    NSURLRequest *req = [NSURLRequest requestWithURL:url];

    // create a connection that will exchange this request for data from the url
    connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
    }

  • (void)showInfo:(id)sender
    {
    ChannelViewController *channelViewController = [[ChannelViewController alloc] initWithStyle:UITableViewStyleGrouped];

    if ([self splitViewController]) {
    UINavigationController *nvc = [[UINavigationController alloc] initWithRootViewController:channelViewController];
    NSArray *vcs = [NSArray arrayWithObjects:[self navigationController], nvc, nil];

      // silver challenge ch26: master button
      UIBarButtonItem *bbi = [[[[[[[self splitViewController] viewControllers] objectAtIndex:1] viewControllers] objectAtIndex:0] navigationItem] leftBarButtonItem];
    
      [[self splitViewController] setViewControllers:vcs];
      [[self splitViewController] setDelegate:channelViewController];
      NSIndexPath *selectedRow = [[self tableView] indexPathForSelectedRow];
      if (selectedRow) {
          [[self tableView] deselectRowAtIndexPath:selectedRow animated:YES];
      }
      
      // silver challenge ch26: master button
      [[[[[[[self splitViewController] viewControllers] objectAtIndex:1] viewControllers] objectAtIndex:0] navigationItem] setLeftBarButtonItem:bbi];
    

    } else {
    [[self navigationController] pushViewController:channelViewController animated:YES];
    }

    [channelViewController listViewController:self handleObject:channel];
    }

@end
[/code]

RSSChannel.m

[code]#import “RSSChannel.h”
#import “RSSItem.h”

@implementation RSSChannel

@synthesize parentParserDelegate, title, infoString, items;

  • (id)init
    {
    self = [super init];
    if (self) {
    items = [[NSMutableArray alloc] init];
    }
    return self;
    }

  • (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
    {
    WSLog(@"\t%@ found a %@ element", self, elementName);

    if ([elementName isEqual:@“title”]) {
    currentString = [[NSMutableString alloc] init];
    [self setTitle:currentString];
    } else if ([elementName isEqual:@“description”]) {
    currentString = [[NSMutableString alloc] init];
    [self setInfoString:currentString];
    } else if ([elementName isEqual:@“item”]) {
    RSSItem *entry = [[RSSItem alloc] init];
    [entry setParentParserDelegate:self];
    [parser setDelegate:entry];
    [items addObject:entry];
    }
    }

  • (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
    [currentString appendString:string];
    }

  • (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
    currentString = nil;

    if ([elementName isEqual:@“channel”]) {
    [parser setDelegate:parentParserDelegate];
    [self trimItemTitles];
    }
    }

  • (void)trimItemTitles
    {
    // regex: Author
    // updated for bronze challenge ch26
    // updated again for silver challenge 26: reply
    NSRegularExpression reg = [[NSRegularExpression alloc] initWithPattern:@"(.) :: (?:Re: )?(.) :: ."
    options:0
    error:nil];
    NSRegularExpression shortReg = [[NSRegularExpression alloc] initWithPattern:@"(.) :: (?:Re: )?(.*)"
    options:0
    error:nil];
    for (RSSItem *i in items) {
    NSString *itemTitle = [i title];
    NSArray *matches = [reg matchesInString:itemTitle
    options:0
    range:NSMakeRange(0, [itemTitle length])];
    // find matches using truncated title
    if ([matches count] == 0) {
    matches = [shortReg matchesInString:itemTitle
    options:0
    range:NSMakeRange(0, [itemTitle length])];
    }
    if ([matches count] > 0) {
    NSTextCheckingResult *result = [matches objectAtIndex:0];

          // bronze challenge ch26 begin
          if ([result numberOfRanges] == 3) {
              [i setSubforum:[itemTitle substringWithRange:[result rangeAtIndex:1]]];
              [i setTitle:[itemTitle substringWithRange:[result rangeAtIndex:2]]];
          }
          // bronze challenge ch26 end
          
          // gold challenge ch26 begin
          int currentIndex = [items indexOfObject:i];
          if (currentIndex > 0) {                 // skip the first object
              
              // previous item
              RSSItem *pi = [items objectAtIndex:currentIndex - 1];
              
              if ([[i title] isEqual:[pi title]]) {  // we found a child post
                  if ([pi parentPostItem]) {      // the previous item is another child
                      [i setParentPostItem:[pi parentPostItem]];
                  } else {                        // the previous item is parent
                      [i setParentPostItem:pi];
                      [pi setParent:YES];         // set parent flag
                  }
              }
          }
          // gold challenge ch26 end
      }
    

    }
    }

@end
[/code]

RSSItem.h

[code]#import <Foundation/Foundation.h>

@interface RSSItem : NSObject
{
NSMutableString *currentString;
}

@property (nonatomic, weak) id parentParserDelegate;

@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *link;
@property (nonatomic, strong) NSString *subforum; // bronze challenge ch26

// gold challenge ch26 begin
@property (nonatomic, weak) id parentPostItem;
@property (nonatomic, getter = isParent) BOOL parent;

  • (BOOL)isChild;
    // gold challenge ch26 end

@end
[/code]

RSSItem.m

[code]#import “RSSItem.h”

@implementation RSSItem

@synthesize parentParserDelegate, title, link;
@synthesize subforum; // bronze challenge ch26
@synthesize parentPostItem, parent; // gold challenge ch26

  • (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
    {
    WSLog(@"\t\t%@ found a %@ element", self, elementName);

    if ([elementName isEqual:@“title”]) {
    currentString = [[NSMutableString alloc] init];
    [self setTitle:currentString];
    } else if ([elementName isEqual:@“link”]) {
    currentString = [[NSMutableString alloc] init];
    [self setLink:currentString];
    }
    }

  • (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
    [currentString appendString:string];
    }

  • (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
    currentString = nil;

    if ([elementName isEqual:@“item”]) {
    [parser setDelegate:parentParserDelegate];
    }
    }

  • (BOOL)isChild // gold challenge ch26
    {
    if ([self parentPostItem]) { // has pointer to parent
    return YES;
    } else {
    return NO;
    }
    }

@end
[/code]