Use NSXMLParser to only parse the first ten posts, then parse next lot separately
Asked Answered
B

4

5

I am using NSXmlParser to parse through an rss feed. All is working well so far.

I'm anticipating the rss feed will eventually contains dozens/hundreds of posts. My current solution is reading the entire rss feed and displaying the results. However I want to only read the first ten posts (to prevent it parsing potentially hundreds of items). Then at a later time (say when the user reaches the end of the table) to parse the next ten posts.

So my question is how would I parse the first ten posts, then parse the next ten posts, then the next ten posts and so on...

Here is what I am using to get ALL posts:

- (void)parseXMLFileAtURL:(NSString *)URL
{   
    myArray = [[NSMutableArray alloc] init];

    //convert the path to a proper NSURL or it won't work
    NSURL *xmlURL = [NSURL URLWithString:URL];

    rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
    [rssParser setDelegate:self];
    [rssParser parse];

}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
    //error
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{            

    currentElement = [elementName copy];

    if ([elementName isEqualToString:@"item"]) {
        //clear out our story item caches...
        item = [[NSMutableDictionary alloc] init];

    }

}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{     

    if ([elementName isEqualToString:@"item"]) {
        // save values to an item, then store that item into the array...
    }

}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{

    // save the characters for the current item...

}

- (void)parserDidEndDocument:(NSXMLParser *)parser {

    [myTable reloadData];

}
Bantam answered 3/7, 2012 at 19:1 Comment(0)
B
7

In a different style answer from the others, I want to contribute that if your parser is taking this long to parse where the difference between 10 and 100 is a significant human-measurable quantity, then you are doing something wrong. It is probably best to:

  1. Profile your parsing code to find the slowdown (or find a more performant XML library)
  2. Parse the entire lot on a background thread
  3. Only display the first 10 to the user,
  4. Display the rest from memory as they go.

It is a double win because parsing it all at once makes for simpler code and less bugs, and "loading" (showing) an rss "page" one at a time will be lightning fast and your users will love you (see Instagram for examples on faking speed)

Boarer answered 8/7, 2012 at 2:9 Comment(1)
Definitely this. You want to use our good friend dispatch_async. If you're not familiar with GCD, I suggest watching the WWDC videos on the matter and prepare to get your mind blown.Addict
G
0

You can call [rssParser abortParsing] to stop the parsing. After you stopped it, you need to manually cut the first part of the xml. Or tell the webservice somehow. The NSXMLParser won't give you an access to jump there.

Goa answered 6/7, 2012 at 14:37 Comment(0)
R
0

Definitely make a counting var in your class header:

...
@private
  NSUInteger rssCounter;
...
@property (nonatomic, assign) NSUInteger rssCounter;
...

In your delegates init method:

rssCounter = 0;

Then simply increment this variable whenever you hit the end of an element, then as stated by @Metalmi, use abort parsing in an if else block when starting an element.

After that, create a method which resets the variable to 0 when you need to load 10 more, which you can call when you need to trigger the whole process again.

Something like:

- (void)restartParsing:(NSString *)url
{
  rssCounter = 0
  [self parseXMLFileAtURL:url];
}

Hope this helps.

Russelrussell answered 7/7, 2012 at 21:59 Comment(4)
I guess this will cause performance problems while parsing second half of xml, otherwise it will be ok to parse it all in one stepArchdeaconry
@MarkPervovskiy I do not know why this would cause performance problems: could you clarify?Russelrussell
Parsing XML is a slow operation, and can take any amount of time, especially when xml contains "hundreds of posts". So, your solution leads to parsing first 10 posts, then first 20 posts, then first 30 posts and so on. One moment user will see how his/hers ui freezes for a half of second.Archdeaconry
Thank you @MarkPervovskiy For some reason I had it in my mind abortParsing acted more like a pauseParsing method. Fail! NSXMLParser is limited because you simply can't pause it. I say either parse the whole shebang or use libxml.Russelrussell
A
0

I agree with @coneybeare, it seems to be the right way to parse xml in background thread and store the result in memory.

Nevertheless, if you still want to parse xml "page by page" you can do it by using xmlParseChunk from libxml (see XMLPerformance sample for example). You will need to manually calculate items and store the surplus somewhere, but I guess it is the closest functionality you can get out of the box.

Another way is to take any Objective-C free open source xml parser (may be even DOM-parser, as example TBXML) and to break its internal parsing loop apart in a way that suits you.

Archdeaconry answered 8/7, 2012 at 12:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.