33

I'm trying to send a HTTP request with NSURLSession. It works fine, but when the server doesn't respond I can't find where the HTTP error code is stored. The third parameter of completionHandler is just a very general NSError. I read the reference of NSURLResponse but found nothing.

NSURLSessionDataTask *dataTask =
    [session dataTaskWithRequest:[self postRequestWithURLString:apiEntry parameters:parameters]
         completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
             if(!error) NSLog([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);    
         }
    ];
[dataTask resume];
Lai Yu-Hsuan
  • 27,509
  • 28
  • 97
  • 164

5 Answers5

56

The second parameter of the completionHandler is the NSURLResponse, which when doing a HTTP request, is generally a NSHTTPURLResponse. So, you'd generally do something like:

NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:[self postRequestWithURLString:apiEntry parameters:parameters] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

    // handle basic connectivity issues here

    if (error) {
        NSLog(@"dataTaskWithRequest error: %@", error);
        return;
    }

    // handle HTTP errors here

    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {

        NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];

        if (statusCode != 200) {
            NSLog(@"dataTaskWithRequest HTTP status code: %d", statusCode);
            return;
        }
    }

    // otherwise, everything is probably fine and you should interpret the `data` contents

    NSLog(@"data: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
[dataTask resume];
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • adding && response == nil to if ([response isKindOfClass:[NSHTTPURLResponse class]]) may be an added advantage – prodeveloper Mar 19 '15 at 06:34
  • 1
    Methinks not. `if ([response isKindOfClass:[NSHTTPURLResponse class]] && response == nil)` would never be true (either `response` is `nil` or it may be HTTP response, but never both at the same time). Maybe you meant `response != nil`, but that's implicit in the call to `isKindOfClass` (i.e. if `response` is `nil`, calling `isKindOfClass` on `nil` object is not true). If you really want `if (response != nil && [response isKindOfClass:[NSHTTPURLResponse class]])`, there's nothing wrong with that and makes the intent explicit, but is redundant. – Rob Mar 19 '15 at 12:06
  • 1
    Yes you are right. There is a typo in the comment. I mean to mention "|| response" instead of "&& response" – prodeveloper Mar 20 '15 at 12:00
  • Would it be correct to say the error means either timeout or no internet connection, and any other kind of error, would be handled through 400 or 500 status codes? So just like you did, you must exit early by **first** checking for errors...then check for any non-200 status code? – mfaani Aug 12 '17 at 20:08
9

Swift 3:

// handle basic connectivity issues here
guard error == nil else {
    print("Error: ", error!)
    return
}

// handle HTTP errors here
if let httpResponse = response as? HTTPURLResponse {
    let statusCode = httpResponse.statusCode

    if (statusCode != 200) {
        print ("dataTaskWithRequest HTTP status code:", statusCode)
        return;
    } 
}

if let data = data {
    // here, everything is probably fine and you should interpret the `data` contents
}
MrAn3
  • 1,335
  • 17
  • 23
2

you could try something like this. I've created a simple method that will be able to post a data into server and get the server response. You can get the server status code via NSHTTPURLResponse class. Hope will help :)

-(void) POST:(NSURL *) url URLparameters:(NSString *) parameters success:(void (^)(NSURLSessionDataTask *  task, id   responseObject)) successHandler errorHandler:(void (^)(NSURLSessionDataTask *  task, NSError *  error)) errorHandler{
     requestBody = [[NSMutableURLRequest alloc]
                   initWithURL:url
                   cachePolicy: NSURLRequestUseProtocolCachePolicy
                   timeoutInterval:60.0];
    [requestBody setHTTPMethod:@"POST"];
    [requestBody setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [requestBody setHTTPBody:[NSData dataWithBytes:
                              [parameters UTF8String]length:strlen([parameters UTF8String])]];
    NSURLSession *session = [NSURLSession sessionWithConfiguration: sessionConfiguration delegate: self delegateQueue: [NSOperationQueue mainQueue]];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:requestBody completionHandler:
                                  ^(NSData *data, NSURLResponse *response, NSError *error) {
                                      NSHTTPURLResponse* respHttp = (NSHTTPURLResponse*) response;

                                      if (respHttp.statusCode == SUCCESS) {
                                          NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                          successHandler(task, dictionary);
                                          NSLog(@"HTTP_STATUS: success %@", response);
                                      }else if (respHttp.statusCode == UNAUTHORIZE) {
                                          NSLog(@"HTTP_STATUS: anauthorize");
                                      }else if (respHttp.statusCode== BAD_REQUEST) {
                                          NSLog(@"HTTP_STATUS: badrequest");
                                      }else if (respHttp.statusCode == INTERNAL_SERVER_ERROR) {
                                          NSLog(@"HTTP_STATUS: internalerror");
                                      }else if (respHttp.statusCode== NOT_FOUND) {
                                          NSLog(@"HTTP_STATUS: internalerror");
                                      }
                                      errorHandler(task, error);
                                      return;
                                  }];
    [task resume];
}
vidalbenjoe
  • 921
  • 13
  • 37
1

If server-side error occurred data parameter from completion handler may contain some useful info

In general I thin you should implement URLSession:task:didCompleteWithError: from NSURLSessionTaskDelegate protocol in session delegate

docs: NSURLSessionTaskDelegate Protocol Reference

sage444
  • 5,661
  • 4
  • 33
  • 60
0

You can have the entire headers in allHeaderFields

let realResponse = response as? NSHTTPURLResponse 
realResponse.allHeaderFields

kindly convert it to objective-C.

Code Tree
  • 2,160
  • 4
  • 26
  • 39