Reputation: 33
I have 2 classes, NSObject
class DataParser
and a ViewController
. In DataParser
class I have some methods that parses XML and results are saved into an array which is a property of DataParser
class. I want to show results to user in my ViewController
. I have here this property @property (strong, nonatomic) DataParser *parser;
and in viewWillAppear
method I created instance of DataParser
class and I'm trying to get array with results this way
self.parser = [[DataParser alloc] init];
[self.parser downloadBooksXML];
NSLog(@"%@", self.parser.array);
but I'm still getting nil. Does anyone know where could be the problem?
- (void)downloadBooksXML
{
NSURL *url = [NSURL URLWithString:@"http://..."];
[self downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
if (data) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
self.foundValue = [[NSMutableString alloc] init];
[self.xmlParser parse];
} else {
NSLog(@"Error");
}
}];
}
EDIT: When I nslog array in DataParser
class in parserDidEndDocument
method, it contains data
Upvotes: 3
Views: 65
Reputation: 13458
Given that you're likely setting your DataParser array property somewhere in your XML parser, and that downloadBooksXML is performing the download and parsing asynchronously, you will find a nil value immediately upon return of the call to downloadBooksXML
.
The delegation pattern suggested by @Aaron works. You could also consider adding your own completion handler to your downloadBooksXML
method, something like this:
typedef void (^DownloadBooksCompletionHandler)();
- (void)downloadBooksXMLWithCompletionHandler:(DownloadBooksCompletionHandler)completionHandler
{
NSURL *url = [NSURL URLWithString:@"http://..."];
[self downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
if (data) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
self.foundValue = [[NSMutableString alloc] init];
[self.xmlParser parse];
} else {
NSLog(@"Error");
}
completionHandler(); // <-- call the completionHandler!
}];
}
then you can "notify" the caller when the download is complete by calling completionHandler()
, and access the array and update your UI.
[self.parser downloadBooksXMLWithCompletionHandler:^()
{
// now OK to access array property and update your UI
}];
you might also consider passing back an NSError * to the completion handler as a means of passing error info.
Lastly, beware of threading issues...completionHandler()
will run on the thread called by your downloadDataFromURL completion handler. Depending on your implementation, this may or may not be the UI thread.
Upvotes: 1
Reputation: 219
NSXMLParser is XML parser (SAX type parser). It starts reading your XML from the beginning, and every time it finds a new element, a closing element or character data, it informs you about it. When parsing your example XML it will call it's delegates:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
if (elementName == @"YourElement") {
// save the elements of attributeDict in your array! This is the place where you add the object to your property self.parser.array!
}
}
PS: If the guy who voted (-1) can tell me what was wrong in my answer?
Upvotes: -1
Reputation: 66234
When [self.parser downloadBooksXML]
returns, self.parser.array
has not been set yet, because the data has not been downloaded and parsed.
The data will be available once the document is done parsing, which you can check using the delegate method:
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(@"%@", self.parser.array);
}
You can therefore use a pattern like this:
- (void) viewDidLoad {
[super viewDidLoad];
self.parser = [[DataParser alloc] init];
[self.parser downloadBooksXML];
}
- (void) dataFinished {
NSLog(@"%@", self.parser.array);
// update the user interface
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[self dataFinished];
}
Depending on the implementation of your other code, you might need to switch to the main queue to update the UI:
- (void) dataFinished {
NSLog(@"%@", self.parser.array);
dispatch_async(dispatch_get_main_queue(), ^(void){
// update the user interface
});
}
Upvotes: 3