Does anyone know in which situations initializing a NSURLConnection returns nil instead of the created connection. The documentation says it's possible but fails to specify when this happens.
The method/message in question:
[[NSURLConnection alloc] initWithRequest:request delegate:self];
-
According to the NSURLConnection Class Reference:
Return Value: The URL connection for the URL request. Returns nil if a connection can't be initialized.
The URL Loading System Programming Guide says the following:
If NSURLConnection can’t create a connection for the request, initWithRequest:delegate: returns nil.
-
While it's possible that this method returns nil, I'm unable to come up with a scenario which triggers this. I've tried the following scenarios:
URLRequest with an empty url: connection:didFailWithError: delegate method is called with "unsupported URL" as error.
URLRequest with invalid url: connection:didFailWithError: delegate method is called with "bad URL" as error.
URLRequest with nonexistent url: connection:didFailWithError: delegate method is called with "A server with the specified hostname could not be found." as error.
Valid request but no internet: connection:didFailWithError: delegate method is called with "The Internet connection appears to be offline." as error.
nil request: causes a crash.
The initWithRequest method returned a valid NSURLConnection in each scenario (besides the last one) and called the connection:didFailWithError: with an appropriate error.
Has anybody been able to figure out which scenario does cause nil to be returned?
I believe this can also be used when it fails to load, not just initialize. (The alloc is done separately - that's where low mem would probably bite you) So (I'm guessing) it could fail because you did not have a network available (3G/Wifi) or it simply failed to connect to the server. In any event, use:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
To get the actual failure.
You may be able to simulate this with an absence of a network - or even giving it a bad URL.
I guess the answer is "Never". Seems only way for NSURLConnection to return nil is failing at [super init]. ([super init] returning nil) But as super class of NSURLConnection is NSObject and NSObjects init just returns self (never nil)
PS: That's for IOS SDK 4.0, on emulator, can be different on device.
I would try all of the above except do it during low memory conditions. IE, I think it will happen when an internal malloc fails.
I thing this is caused if the request u specified has wrong or not all essential values
NSURLConnection returns nil if it is not created inside a runLoop.
Related
So for an iOS client of mine, I'm initiating a POST to upload a big chunk of data (say, 30MB), via:
[[NSURLConnection connectionWithRequest:req delegate:self] retain];
However, I need to handle the case where the URL is bad - say, one that returns a 404. The problem is, my didSendBodyData delegate method is called over and over, just like it should be assuming the URL is correct, in chunks of 32k worth of data, until it hits the 30MB total file size. I'm actually doing calculations in this method to show and display a progress bar, and even with an invalid URL, it looks and acts like it's transferring the whole file (I'm not sure what's really going on under the covers - it can't actually be doing anything, the URL resolves to a 404!)
THEN, after 20 seconds of supposed uploading, my didReceiveResponse delegate method is finally called, where I can check the NSURLResponse and see that a 404 was served up. Why does it seem like it transfers the entire file before I'm able to get any kind of error? Using [NSURLConnection connectionWithRequest], how can I know this was a 404 before it actually does anything? Is there anyway to tell in the didSendBodyData method, or any other way to tell earlier than what I'm seeing?
An even bigger problem is, I let you limit the upload time, so it stops after x number of seconds, at which point I cancel the connection (in the middle of the didSendBodyData method), so doing that there's seemingly no way to know anything even went wrong with the URL.
It seems like this should have a ridiculously easy solution, but I'm not seeing it. Thanks for any help.
Based on the spec, I don't believe that an HTTP server sends back an HTTP response until the HTTP request has been completely received. This means that even though you are sending a bunch of data to nowhere, the behavior you are seeing is expected.
You might try sending the HTTP Expect Header in your request. Otherwise, have you considered sending out a preliminary ping request to check if the url endpoint exists? Maybe a POST with no data? Then you can find out about the 404 before making the costly request.
Have you implemented the NSURLConnectionDataDelegate ?
As the following delegate methods could be what you're after:
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
if you just want to examine the response code and perform some action then I would add the following to your code
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == 404){
NSLog(#"Epic Fail");
}
...
...
}
Background: During the handshake process, my iOS app learns the server capabilities by
Checking its header information in didReceiveResponse
AND
Parsing its response XML in connectionDidFinishLoading
Problem statement: Since callback #1 precedes #2, I am storing the version string in didReceiveResponse and checking it later in connectionDidFinishLoading when the response is available.
This fortunately works fine so far because #1 preceeds #2. But is that order always guaranteed by network / iOS?
From NSURLCOnnection Reference:
Zero or more connection:didReceiveResponse: messages will be sent
to the delegate before receiving a connection:didReceiveData: message.
The only case where connection:didReceiveResponse: is not sent to a
delegate is when the protocol implementation encounters an error
before a response could be created.
Zero or more connection:didReceiveData: messages will be sent
before any of the following messages are sent to the delegate:
connection:willCacheResponse:, connectionDidFinishLoading:,
connection:didFailWithError:.
Zero or one connection:willCacheResponse: messages will be sent to
the delegate after connection:didReceiveData: is sent but before a
connectionDidFinishLoading: message is sent.
So, it will not happen ONLY if there is an error before creating the response.
According to the documentation:
Zero or more connection:didReceiveResponse: messages will be sent to the delegate before receiving a connection:didReceiveData: message. The only case where connection:didReceiveResponse: is not sent to a delegate is when the protocol implementation encounters an error before a response could be created.
Zero or more connection:didReceiveData: messages will be sent before any of the following messages are sent to the delegate: connection:willCacheResponse:, connectionDidFinishLoading:, connection:didFailWithError:.
So unless there's an error, you can guarantee that you'll get a didReceiveResponse before getting a connectionDidFinishLoading.
Yes, of course. In didReceiveResponse you could check NSURLResponse and if something will be incorrect you can just stop downloading. So connectionDidFinishLoading will be called only after you've checked the response.
I have a few methods that are posting data to a server. I am using NSURLConnection asynchronously. I am using a class variable NSMutableData and this is created in the viewDidLoad.
I have one method connection created for login and one for registration. Both just return a success booleon in response.
My question is, when the connection is complete and I receive my response, is there anyway to know which method started the connection? I.e some kind of callback to say that the login method started the connection.
Thanks
When you start the connection, store a reference to the NSURLConnection object. When the connection finishes it will pass you the connection object as a parameter, which you can check like so:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection==loginConnection) {
} else if (connection==regConnection) {
}
}
The other (possibly better) option is to offload all of the HTTP connection handling to a separate wrapper class. You can build your own from your existing code, or use a third-party one like ASIHTTPRequest or GTM HTTP Fetcher.
I've dug through a lot of topics regarding didReceiveData: method not being called - the solution is to set appropriate cachePolicy and implement willCacheResponse delegate method.
So I did, now I get didReceiveData message each time anything comes from the server.
Unfortunately I don't get didReceiveData the very first time the server responds.
In other words, the first time I get "didReceiveData" message there are 2 response objects in the "data" parameter. Every response from the server following that moment is OK.
Any help?
Just needed a clarification; Are you using any threading in your application?
If Yes, then the thread which creates the connection should receive the response from the server. The other threads will not identify the response which is returned for some other thread.
I'm writing an iPhone application that needs to get some data from a web server. I'm using NSURLConnection to do the HTTP request, which works well, but I'm having trouble unit testing my code in the case where the response has an HTTP error code (like 404 or 500).
I'm using GTM for unit testing and OCMock for mocking.
When the server returns an error, the connection does not call connection:didFailWithError: on the delegate, but calls connection:didReceiveResponse:, connection:didReceiveData:, and connectionDidFinishLoading: instead. I'm currently checking the status code on the response in connection:didReceiveResponse: and calling cancel on the connection when the status code looks like an error to prevent connectionDidFinishLoading: from being called, where a successful response would be reported.
Providing a static stubbed NSURLConnection is simple, but I want my test to change it's behaviour when one of the mock connection's methods is called. Specifically, I want the test to be able to tell when the code has called cancel on the mock connection, so the test can stop calling connection:didReceiveData: and connectionDidFinishLoading: on the delegate.
Is there a way for tests to tell if cancel has been called on the mock object? Is there a better way to test code that uses NSURLConnection? Is there a better way to handle HTTP error statuses?
Is there a better way to handle HTTP error statuses?
I think you are on the right track. I use something similar to the following code, which I found here:
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];
}
}
This cancels the connection, and calls connection:didFailWithError: in order to make http error codes behave exactly the same as any other connection error.