Dan Wood: The Eponymous Weblog (Archives)

Dan Wood Dan Wood is co-owner of Karelia Software, creating programs for the Macintosh computer. He is the father of two kids, lives in the Bay Area of California USA, and prefers bicycles to cars. This site is his older weblog, which mostly covers geeky topics like Macs and Mac Programming. Go visit the current blog here.

Useful Tidbits and Egotistical Musings from Dan Wood

Categories: Business · Mac OS X · Cocoa Programming · General · All Categories

Wed, 28 Feb 2007

Q: When is an connection error not an error?

A: When it's a status code.

If you are using NSURLConnection for accessing data over HTTP, as I like to do from time to time, you may find yourself scratching your head wondering why you didn't receive the connection:didFailWithError: callback in your delegate when clearly there was an error retrieving the resource you were looking for.

The reason is that connections that return an error as its status code (such as a 404 "Not Found" or 500 "Internal Server Error") don't result in a failure callback.

What you need to do is check for status codes in your implementation of connection:didReceiveResponse:. If you are fetching a resource over HTTP, then your NSURLResponse in the callback will actually be NSHTTPURLResponse, and you can query the status code. Any value greater than or equal to 400 is generally an error.

My general pattern is to cancel the connection, package up an NSError object based on that status code, and call the connection:didFailWithError: callback myself. This way I can treat status-code errors the same as I would another kind of error. (I have appropriated the string constant NSHTTPPropertyStatusCodeKey, normally used by NSURLHandle, as the error domain string.)

if ([response respondsToSelector:@selector(statusCode)])
{
  int statusCode = [((NSHTTPURLResponse *)response) statusCode];
  if (statusCode >= 400)
  {
    [connection cancel];  // stop connecting; no more delegate messages
    NSDictionary *errorInfo
      = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:
          NSLocalizedString(@"Server returned status code %d",@""),
            statusCode]
                                    forKey:NSLocalizedDescriptionKey];
    NSError *statusError
      = [NSError errorWithDomain:NSHTTPPropertyStatusCodeKey
                            code:statusCode
                        userInfo:errorInfo];
    [self connection:connection didFailWithError:statusError];
  }
}

Side note: Don't forget that you may recieve the connection:didReceiveResponse: callback multiple times, in the case of redirects!