Does
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
return error = nil when data is not nil?
if there is data returned, is error always nil?
that's what the documentation says ...
how is error affected by the http status code?
like if the status code is not 200, does it always return an error?
The NSError is not related with the HTTP Response. The NSError is returned when the connection itself failed. A bad HTTP status code means the request itself works for the client.
You have to manually check the status code.
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = [httpResponse statusCode];
Now you can test if the status is 200.
To answer to your questions :
return error = nil when data is not nil?
if there is data returned, is error always nil?
If the data == nil then you should have error != nil
how is error affected by the http status code? like if the status code
is not 200, does it always return an error?
If you got a status code then it means the connection worked (from a connection request point of view, not the HTTP one) so error == nil and data should be not nil
You answered your first two questions yourself already. The documentation is right there.
Regarding the status code: No, status codes do not affect the NSError at all. This NSError is reserved for when a NSURLConnection actually fails in a non-HTTP way. For example when you're actually offline and no request can be made at all. Also keep in mind, that NSURLConnection is not tied to HTTP requests in any way. iOS also supports FTP connections out of the box (no status code like in HTTP here) and you can even write your own protocol support for almost anything you can think of.
Of course there will just occure an NSError if the request failed to get the data. But if the server returns you any other status code than 200, it will be like an error because you couldn't get the data. But by checking the status code you can give more specific error messages to the user instead of just Couldn't load data, because you can specify if there was an NSError which could be caused by missing internet connection or if you received any bad status code and the server is not available..
Related
When API request fails, I need to know number of failed attempts. Backend has added a property ‘attempts’ to error object. How can I find this value, since NSError is not KV pairing compliant and I don’t see it when I log the error object to console.
If you are using NSURLSession for urlrequest , then you will have delegate methods for NSURLSession responses , they are:
->didReceiveResponse = here you receive the response that urlrequest has completed and response received status
->didReceiveData = here you get the SUCCESS response date for your request
->didCompleteWithError = here you get the FAILURE response (i.e) ERROR callback ,this is called every time when request fails , which gives you error data
This may be a very silly question!! But, as I am having a doubt, thought of asking here.
Are the two delegate methods (i.e. connection:didReceiveResponse: and connection:didFailWithError:) are mutually exclusive to each other? I mean is there any scenario in which both the delegate methods could get called by NSURLConnection object?
In case only connection:didFailWithError: gets called, how to retrieve the HTTP status code?
From the docs:
The only case where this message is not sent to the delegate is when
the protocol implementation encounters an error before a response
could be created.
NSURLConnectionDelegate Class Reference
So, a NSURLConnection could fail before it gets a response, however it could fail after. Since connection:didFailWithError ceases any further messages for that connection, the following two scenarios could happen:
The connection is started, fails to get a response and connection:didFailWithError is called.
The connection is started and gets a response, connection:didReceiveResponse: is called, then the connection fails for some reason (The network connection could drop out for example), before connectionDidFinishLoading is called.
You will need to get the HTTP status code from connection:didReceiveResponse, if that method isn't called, there is no status code as it is part of the response. If you need to access it from within connection:didFailWithError you will need to record it.
Note that to get the status code, you will also need to case NSURLResponse to NSHTTPURLResponse like so:
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(#"Status code %ld", httpResponse.statusCode);
I am confused about this method, specially about the error part and the documentation. Here on the documentation it says that
If the request completes successfully, the data parameter of the
handler block contains the resource data, and the error parameter is
nil. If the request fails, the data parameter is nil and the error
parameter contain information about the failure.
The problem is that, I have seen that some http response error codes fall into the "there is an error" and some dont, furthermore I can have data and error at the same time (but supposedly one should be nil at all times according to the documentation).
[NSURLConnection sendAsynchronousRequest:theRequest
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
For example, my server sends a 401 response if the user uses wrong credentials, and this is cached inside the error clause, but if i send a 409 (duplicate entry) this is NOT cached by it. Im having some issues redirecting the flow because of this, im not sure what this method will consider an error and what it will not.
Also, how do i properly check for this?, I thought that just checking if there is an error, and then checking what was the code of the response would suffice to display a message or take an action, but as I described before, some "error" codes don't necessarily generate the error object. Should i first check for the status code?
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = [httpResponse statusCode];
if (responseStatusCode == 200)
{
// If 200, assume everything went well?
} else
{
// Something went wrong, check for the code and the error here?
}
From my experience an error occurs when there is no response at all from the server, that's why the data is 0. If a server returns 400 or 500 error then these are a response from your server and depending on your server code maybe an error message you can parse.
Several times before I used the method...
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
to send/receive data synchronously when in an NSOperation and it has worked.
I'm working on a new project (new API, new server) now and the error just isn't being populated.
I'm using Charles proxy to snoop the network traffic and I can see "Response Code 500 Internal Server Error" coming back from the failed request in Charles but in the app the error object is always nil.
Like I said, I've used this loads before and the error is always populated if an error occurs.
Is there something on the server side that I can point out to the server side dev? He's adamant that I must be doing something wrong. The only thing I don't understand is that Charles is picking up response code 500 from it.
N.B. The problem is not that the error is occurring. The problem is that when it does occur the NSError object is not reporting it back to me. Just to clear it up in case anyone tells me to ask the server dev to fix the error. :-)
Your error object is not populated because the request succeeded from NSURLConnection's perspective -- that is, it was able to send data to the server and receive a response.
For protocol-specific errors, you need to inspect the NSURLResponse you get back. In this case, cast the response to NSHTTPURLResponse and invoke -statusCode.
The error is only populated if there is no response from the server or not a valid HTTP response, e.g. connection loss etc.
"Response Code 500 Internal Server Error" means there was an internal server error and your server returns an HTTP message with the response code 500. You should use NSHTTPURLResponse instead of NSURLResponse and call the statusCode method to check the HTTP response code. If the response code starts with 2 everything is fine. So I usually have a check like this: statusCode / 100 == 2.
For more HTTP response codes see http://en.wikipedia.org/w/index.php?title=HTTP_response_codes
While downloading a file from Amazon S3 in iOS. Intermittently NSURLConnectionDownloadDelegate's method didFailWithError: get called and this is what I got when I logged received NSError object
Error Code: 109
Error Domain: SSErrorDomain
Error Description: "Cannot connect to .s3.amazonaws.com"
Searched all the Apple documentation, StackOverflow and other sites but not found anything on this. Today I raised a technical query to Apple also for this using my developer account.
Any idea ?
Update:
So after looking into HTTP response error code (403 Forbidden), I got the idea. It is because of "RequestTimeTooSkewed" error from S3 (The difference between the request time and the current time is too large.). I cross checked it by changing iPad's/Mac's system time by 1 hour and this error is coming immediately now, even for a small (200kb) file.
Now as suggested in many blogs I am first making a HEAD request to AWS as below to get the Date string and not passing the system Date
NSString *awsURL = #"http://s3.amazonaws.com";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:awsURL]];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];
NSString *dateString = nil;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
dateString = [[response allHeaderFields] objectForKey:#"Date"];
}
return dateString;
and setting this as Date header in NSMutableURLRequest
[urlRequest setValue:awsDateString forHTTPHeaderField:#"Date"];
This request I am adding to my issue for download
NKAssetDownload *nkAssetDownload = [nkIssue addAssetWithRequest:urlRequest];
Still the same error !!!! It now more crazier than my last situation.
Anyone ?
Update 2
I was able to make request successfully (even the system clock of my iPad is incorrect) by replacing "GMT" with "+0000" in the date string.
Update 3
Still some requests fail with same error which is weird, but I am assuming it is something the NewsStand Framework is messing up.
So it is RequestTimeTooSkewed error and the above code to fetch date from S3 server's head response to add in request does the trick.