views:

436

answers:

2

How can I set a timeout when I am parsing a feed using initWithContentsOfURL. Sometimes our feeds return a blank response and then it will just try to parse the feed forever. I want to set a timeout of 30 seconds or so that will pop up a UIAlertView and then try and reparse the feed.

    NSURL *feedURL = [[NSURL alloc] initWithString:URL];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:feedURL];
    [parser setDelegate:self];
    [parser setShouldProcessNamespaces:NO];
    [parser setShouldReportNamespacePrefixes:NO];
    [parser setShouldResolveExternalEntities:NO];

    [parser parse];
+1  A: 

First approach: using a delayed selector

Probably the simplest way to do this is to use NSObject's performSelector:withObject:afterDelay: method. You can define some method parsingDidTimeout as such:

- (void)parsingDidTimeout {
    if(self.parsingDidComplete == NO) {
        [self.parser abortParsing];
        // Create your error and display it here

        // Try the fetch and parse again...
    }
}

This requires that you hang on to the parser as an instance variable (self.parser), so that you can cancel it from the method you define. It also requires that your parser delegate keep track of whether or not the parser has finished (self.parsingDidComplete, can be defaulted to NO and set to YES in the delegate's parserDidEndDocument: method). This is to avoid aborting a successful parse. After this is done, all it takes is a simple

[self performSelector:@selector(parsingDidTimeout) withObject:nil afterDelay:30];

and thirty seconds later, your parsing-abort code will get called, and you can do whatever it is you need to do.

Second approach: using a timer

You could make this whole approach (arguably) simpler in the timeout method by using an NSTimer instead of the NSObject method call. That way, if the parser successfully finishes, you can simply invalidate the timer, allowing you to eliminate the if clause in the parsingDidTimeout method (and, as a result, also getting rid of the BOOL ivar). The timer initialization would look like:

NSTimer *timer = [NSTimer timerWithTimeInterval:30.0
                                         target:self
                                       selector:@selector(parsingDidTimeout)
                                       userInfo:nil
                                        repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
Tim
+1  A: 

Doesn't answer your question direction, but you'd have complete control over the request and response cycle (as well as asynchronicity without additional threads) if you used NSURLConnection to download the data. That's what initWithContentsOfURL: is doing, under the covers.

Sixten Otto