FetchEntries Add Rows At Bottom Of Already Fetched Ones


#1

I have slightly modified the FetchEntries method to add more rows at the bottom of the already fetched entries from another page of the rss feed.

In ListViewController.m : I am counting the total number of entries that it has fetched from the web service, since one page of the rss feed contains 10 entries, i am dividing the total number with 10 and incrementing the counter by 1 to get the next page.

[code]- (void) fetchEntriesNew:(NSInteger )pageNumber
{

channel = [[TheFeedStore sharedStore] fetchWebService:pageNumber withCompletion:^(RSSChannel *obj, NSError *err){
    
        if (!err) {
            int currentItemCount = [[channel items] count];
            channel = obj;
            int newItemCount = [[channel items] count];
            NSLog(@"Total Number Of Entries Are: %d", newItemCount);
            counter = (newItemCount / 10) + 1;
            NSLog(@"New Counter Should Be %d", counter);

            int itemDelta = newItemCount - currentItemCount;
            if (itemDelta > 0) {
                
                NSMutableArray *rows = [NSMutableArray array];
                
                for (int i = 0; i < itemDelta; i++) {
                    NSIndexPath *ip = [NSIndexPath indexPathForRow:i inSection:0];
                    [rows addObject:ip];
                }
                [[self tableView] insertRowsAtIndexPaths:rows withRowAnimation:UITableViewRowAnimationBottom];
            }
        }
        else {
            
            // If things went bad, show an alert view
            NSString *errorString = [NSString stringWithFormat:@"Fetch failed: %@",
                                     [err localizedDescription]];
            
            // Create and show an alert view with this error displayed
            UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error"
                                                         message:errorString
                                                        delegate:nil
                                               cancelButtonTitle:@"OK"
                                               otherButtonTitles:nil];
            [av show];
        }
}];
[[self tableView] reloadData];

}
[/code]

In TheFeedStore.m, i have changed the code that will take the counter and get the next page of the feed:

- (RSSChannel *)fetchWebService:(NSInteger)count withCompletion:(void (^)(RSSChannel *, NSError *))block { NSString *requestString = [NSString stringWithFormat:@"http://techcrunch.com/feed/?paged=%d", count]; ........... ........... }

Back in ListViewController.m, i am using the scrollview method to determine the end of the scrollview and when the user hits the end, it will use the incremented value of the counter to call fetchentries method to load more rows from the next page of rss feed at the bottom of the already fetched entries.

[code]- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
float endScrolling = scrollView.contentOffset.y + scrollView.frame.size.height;
if (endScrolling >= scrollView.contentSize.height)
{
NSLog(@“Scroll End Called”);
NSLog(@“New Counter NOW is %d”, counter);
[self fetchEntriesNew:counter];
}

}[/code]

SO Far So Good.

But the problem occurs, when user reaches the bottom of the scrollview and scrolls down more than once. Doing so it gets reloaded again and again. If the user scrolls and hits the bottom, it will start putting rows at the bottom but in doing so, if the users scrolls to the bottom and hits the end again, the previous request will stop and it will start loading the rows again. How to prevent this situation?

Anybody mind taking a look into it? Thanks in advance.


#2

Add an isFetching BOOL property so you know when a fetch is already in progress and don’t clobber it on repeated scroll-to-end.

You could also add a Twitter style spinner in the last cell when loading more to provide progress feedback to the user.

As an alternative to all the above, you could do a Tumblr page-1 style “tap to load more” button of a last cell rather than using scrolling to bottom to trigger the load.

Tumblr also triggers loading the next page of content by getting near, rather than at, the bottom. I believe Twitter has moved to doing this as well. The end result is that you never hit the bottom and see “loading”, you just keep scrolling and finding new content has magically appeared.

For a challenge, add state restoration that preserves the location in the RSS feed, so the user doesn’t lose their place when the app gets terminated.


#3

First of all thanks a lot for the reply. Let me sum up the solutions you have provided in your answer.

  1. A Bool property that switches between YES and NO to check when it should load more cells and when it shouldn’t.
  2. Add a “Load More” button at the bottom which when tapped, starts loading the entries from the next page. (Example: Mashable iPhone App)
  3. Start loading more entries from next pages when user reaches near the bottom or half way down (Example: Tumblr and Instagram)

The third solution is a little more complex and i wouldn’t go for it as right now i want a working app rather than trying more advanced stuff. Other than that, i have considered the first two solution before posting this question and here is the problem: I am also using the UIImageView inside the custom tableview cell. This UIImageView is loading the images asynchronously by using the CDATA delegate method of nsxmlparser. Here is the code: In RSSItem.m:

[code]- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock {

NSString *someString = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];

[currentString appendString:someString];

NSString *imageURLString = [self getFirstImageUrl:someString];
NSURL *imageURL = [NSURL URLWithString:imageURLString];
[self downloadThumbnails];

}

  • (void) downloadThumbnails:(NSURL *)finalUrl
    {
    dispatch_group_async(((RSSChannel *)self.parentParserDelegate).imageDownloadGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

      NSMutableData *tempData = [NSData dataWithContentsOfURL];
      
      dispatch_async(dispatch_get_main_queue(), ^{
          
          thumbnail = [UIImage imageWithData];
          
           [[NSNotificationCenter defaultCenter] postNotificationName:@"DataSaved" object:nil];
    
       });
    

    });
    }[/code]

If the app i have been working on was just pulling the titles from the RSS feed, putting a “load more” button or similar solution would have worked. But since, the app is also downloading the images asynchronously from the web service, titles and other labels are loaded at once but images are downloaded in a separate thread, and while downloading the images, the user has the chance to reach the bottom, thus incrementing the counter. And when the counter is incremented, if all the images are not downloaded yet at this point, it will stop and ignore the whole page and instead start working on the next one.

I should have mentioned this detail in the very first post but hope the whole problem is quite clear now and someone would be able to point me to the right solution.

Thanks!


#4

After observing again and again and re-reading the MVCS chapter from the book, i am starting to believe that the problem is how the BNRFeedStore and BNRConnection are communicating with each other but i am not sure about that.

The following method that i have modified to take Integer value to handle different pages of the feed is only handling one page at a time:

- (RSSChannel *)fetchWebService:(NSInteger)count withCompletion:(void (^)(RSSChannel *, NSError *))block { NSString *requestString = [NSString stringWithFormat:@"http://example.com/feed/?paged=%d", count]; NSURL *url = [NSURL URLWithString:requestString]; NSURLRequest *req = [NSURLRequest requestWithURL:url]; ............... .............. }

That means when ListViewController asks the BNRFeedStore to handle the second page of the feed, it just forgets about the first that is in the process of loading, instead it stops the previous request and starts handling the new one.

I am not sure whether i am right on this or not. But may be that could help someone to help solve my issue :confused:

I hope the users and admins are reading it and i am not just talking to myself :confused:

Any help would be appreciated. Thanks again!