I'm creating a xml parser class with TBXML. I'd like the class to load an xml doc, traverse through it, and return an array of strings to populate a table. This should take place in a background thread so it does block the UI. I'd like to add a completion block so the table's data source array is set when the xml parsing is complete.
How do I implement the completion block? Here's what I have so far:
Parser.m
- (NSMutableArray *)loadObjects
{
// Create a success block to be called when the asyn request completes
TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
NSLog(@"PROCESSING ASYNC CALLBACK");
// If TBXML found a root node, process element and iterate all children
if (tbxmlDocument.rootXMLElement)
[self traverseElement:tbxmlDocument.rootXMLElement];
};
// Create a failure block that gets called if something goes wrong
TBXMLFailureBlock failureBlock = ^(TBXML *tbxmlDocument, NSError * error) {
NSLog(@"Error! %@ %@", [error localizedDescription], [error userInfo]);
};
// Initialize TBXML with the URL of an XML doc. TBXML asynchronously loads and parses the file.
tbxml = [[TBXML alloc] initWithURL:[NSURL URLWithString:@"XML_DOC_URL"]
success:successBlock
failure:failureBlock];
return self.array;
}
- (void)traverseElement:(TBXMLElement *)element
{
do {
// if the element has child elements, process them
if (element->firstChild) [self traverseElement:element->firstChild];
if ([[TBXML elementName:element] isEqualToString:@"item"]) {
TBXMLElement *title = [TBXML childElementNamed:@"title" parentElement:element];
NSString *titleString = [TBXML textForElement:title];
[self.array addObject:titleString];
};
// Obtain next sibling element
} while ((element = element->nextSibling));
}
TableViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
Parser *parser = [[Parser alloc] init];
self.products = [parser loadObjects];
}
Like this, you'll get an empty array, right? That's because loadObjects is not blocking and returns before it could complete its task.
Now, you want your viewDidLoad to return immediately so your table can display. So in your TableViewController, you want a callback that gets called and manages the update after your empty table is displayed. The standard technique is to use delegation. In Parser.h define
@protocol ParserProtocol <NSObject>
-(void)parserDidFinishLoading;
@end
in the Parser-interface add
@property id<ParserProtocol> delegate;
Finish your successBlock with the line
[self.delegate parserDidFinishLoading];
then make TableViewController conforming to ParserProtocol and add the Parser as a property:
@property Parser* parser;
in TableViewController.m replace
Parser *parser = [[Parser alloc] init];
self.products = [parser loadObjects];
with
self.parser = [[Parser alloc] init];
self.products = [self.parser loadObjects];
and add
-(void)parserDidFinishLoading{
[self.tableView reloadData];
}
The self.products points to the now modified array property of Parser. So no additional setup should be needed.
Good luck, Peter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With