1

Interesting programming puzzle...

I failed to initialize error to nil, so the code below crashes.

    NSString *query = [NSString stringWithFormat:@"http://mysite.com/getlatest.php?a=%@", aSuiteName];
    NSURL *url = [NSURL URLWithString:query];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:10];
    [request setHTTPMethod:@"GET"];
    NSURLResponse *response = nil;
    NSError *error;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    if ( !data || error != nil ) {
        NSLog(@"Error downlading %@", error); //CRASH
        return NO;
    }

My question is, how can I have my server return an error such that sendSynchronousRequest:returningResponse:error: actually receives an error, yet continues to download the data anyway? Some kind of non-fatal error which will result in error being initialized to an NSError object?

This is related to a similar conundrum where I've wanted to return status information without total failure before. So, although obscure, I'm interested to know if there is a technique for this.

Though this is a Cocoa question, I think the answer will be in php. Something like

<?php

    header( set 503 error here );

    return data;

?>
SG1
  • 2,871
  • 1
  • 29
  • 41
  • Every HTTP response has a status code – so the protocol certainly supports sending a 503 along with response data. (I'm using this with Cocoa app I'm developing right now.) Do you need to know how to handle this with NSURLRequest, or how to produce this with PHP? – paulmelnikow May 18 '12 at 16:29
  • I responded to the Cocoa part below. – paulmelnikow May 18 '12 at 16:41
  • I need to know how to handle the php (or some other server setting, I wasn't sure if it was in php code or a control panel). I see now that php is the way to go, setting a header(). – SG1 May 18 '12 at 17:41

1 Answers1

0

I handle this by wrapping the data into an NSError object. In the case of a plain text response, I also use the response as a localizedDescription. I use a separate method to evaluate whether the result is successful. If the URL request itself generates an error, I also consider that to be a failure, and pass along the NSError provided by the URL request.

This is snipped from my ServerRequest class:

+ (NSError *) errorForStatusCode:(NSInteger) statusCode
                     contentType:(NSString *) contentType
                textEncodingName:(NSString *) textEncodingName
                            data:(NSData *) data {

    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    if ([contentType isEqualToString:@"text/plain"]) {
        [userInfo setValue:[[NSString alloc] initWithData:data encoding:[self stringEncodingForTextEncodingName:textEncodingName]]
                    forKey:NSLocalizedDescriptionKey];
    } else if ( ... ) {
        // handle other relevant types         
    }

    return [NSError errorWithDomain:[[self class] description]
                               code:statusCode
                           userInfo:userInfo];
}

- (BOOL) statusCodeIndicatesSuccess:(NSInteger) statusCodeValue {
    return 201 == statusCodeValue; // Modify this based on your API
}

- (BOOL) runSynchronouslyReturningResult:(NSData **) resultPtr error:(NSError **) errorPtr {
    NSHTTPURLResponse *response = nil;
    NSError *error = nil;
    NSData *result = [NSURLConnection sendSynchronousRequest:[self request] returningResponse:&response error:&error];
    if (error) {
        if (errorPtr) *errorPtr = error;
        return NO;
    }

    self.statusCode = [response statusCode];
    self.MIMEType = [response MIMEType];
    self.textEncodingName = [response textEncodingName];
    if ([self statusCodeIndicatesSuccess:self.statusCode]) {
        if (resultPtr) *resultPtr = result;
        return YES;
    } else {
        if (errorPtr) *errorPtr = [ServerRequest errorForStatusCode:self.statusCode
                                                        contentType:self.MIMEType
                                                   textEncodingName:self.textEncodingName
                                                               data:result];
        if (resultPtr) *resultPtr = result;
        return NO;
    }
}
paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
  • But if you can change the Cocoa code, why not just fix the original problem of failing to initialize/over-aggressively checking `error`? – Peter Hosey May 18 '12 at 16:59
  • I cannot change the code. It's being run, for all intents and purposes, on a lunar lander. So now I am - from a million miles away - altering server behavior to avoid the crash. Unfortunately, the method will still return 'NO', but at least it won't crash the app. – SG1 May 18 '12 at 17:35
  • I had no idea you couldn't change the Cocoa code. You did paste code you knew to be broken with a bug that was easily fixed, now I see why. – paulmelnikow May 18 '12 at 18:49
  • Here's an answer about the PHP code: http://stackoverflow.com/questions/6163970/set-response-status-code/6164319#6164319 – paulmelnikow May 18 '12 at 18:53
  • heh, thanks though for providing the very robust correct cocoa answer, which I will probably integrate into my framework now. Both your answers are right. – SG1 May 18 '12 at 21:51