I've read through tons of messages saying the same thing all over again : when you use a NSURLConnection, delegate methods are not called. I understand that Apple's doc are incomplete and reference deprecated methods, which is a shame, but I can't seem to find a solution.
Code for the request is there :
// Create request
NSURL *urlObj = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlObj cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
[request setValue:#"gzip" forHTTPHeaderField:#"Accept-Encoding"];
if (![NSURLConnection canHandleRequest:request]) {
NSLog(#"Can't handle request...");
return;
}
// Start connection
dispatch_async(dispatch_get_main_queue(), ^{
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; // Edited
});
...and code for the delegate methods is here :
- (void) connection:(NSURLConnection *)_connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Receiving response: %#, status %d", [(NSHTTPURLResponse*)response allHeaderFields], [(NSHTTPURLResponse*) response statusCode]);
self.data = [NSMutableData data];
}
- (void) connection:(NSURLConnection *)_connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed: %#", error);
[self _finish];
}
- (void) connection:(NSURLConnection *)_connection didReceiveData:(NSData *)_data {
[data appendData:_data];
}
- (void)connectionDidFinishDownloading:(NSURLConnection *)_connection destinationURL:(NSURL *) destinationURL {
NSLog(#"Connection done!");
[self _finish];
}
There's not a lot of error checking here, but I've made sure of a few things :
Whatever happens, didReceiveData is never called, so I don't get any data
...but the data is transfered (I checked using tcpdump)
...and the other methods are called successfully.
If I use the NSURLConnectionDownloadDelegate instead of NSURLConnectionDataDelegate, everything works but I can't get a hold on the downloaded file (this is a known bug)
The request is not deallocated before completion by bad memory management
Nothing changes if I use a standard HTML page somewhere on the internet as my URL
The request is kicked off from the main queue
I don't want to use a third-party library, as, ultimately, these requests are to be included in a library of my own, and I'd like to minimize the dependencies. If I have to, I'll use CFNetwork directly, but it will be a huge pain in the you-know-what.
If you have any idea, it would help greatly. Thanks!
I ran into the same problem. Very annoying, but it seems that if you implement this method:
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
Then connection:didReceiveData: will never be called. You have to use connectionDidFinishLoading: instead... Yes, the docs say it is deprecated, but I think thats only because this method moved from NSURLConnectionDelegate into NSURLConnectionDataDelegate.
I like to use the sendAsynchronousRequest method.. there's less information during the connection, but the code is a lot cleaner.
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
if (data){
//do something with data
}
else if (error)
NSLog(#"%#",error);
}];
From Apple:
By default, a connection is scheduled on the current thread in the
default mode when it is created. If you create a connection with the
initWithRequest:delegate:startImmediately: method and provide NO for
the startImmediately parameter, you can schedule the connection on a
different run loop or mode before starting it with the start method.
You can schedule a connection on multiple run loops and modes, or on
the same run loop in multiple modes.
Unless there is a reason to explicitly run it in [NSRunLoop currentRunLoop],
you can remove these two lines:
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
or change the mode to NSDefaultRunLoopMode
NSURLConnection API says " ..delegate methods are called on the thread that started the asynchronous load operation for the associated NSURLConnection object."
Because dispatch_async will start new thread, and NSURLConnection will not pass to that other threat the call backs, so do not use dispatch_async with NSURLConnection.
You do not have to afraid about frozen user interface, NSURLConnection providing only the controls of asynchronous loads.
If you have more files to download, you can start some of connection in first turn, and later they finished, in the connectionDidFinishLoading: method you can start new connections.
int i=0;
for (RetrieveOneDocument *doc in self.documents) {
if (i<5) {
[[NSURLConnection alloc] initWithRequest:request delegate:self];
i++;
}
}
..
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
ii++;
if(ii == 5) {
[[NSURLConnection alloc] initWithRequest:request delegate:self];
ii=0;
}
}
One possible reason is that the outgoing NSURLRequest has been setup to have a -HTTPMethod of HEAD. Quite hard to do that by accident though!
I am trying to make a Asynchronous Call , a Synchronous one. I know its not a better idea to do it. But, I do need such to code to handle Auth Challenge of Self Signed Certificate while Keeping the call still as Synchronous.
But, I am not sure whether it is a perfect way to make Asycnh call a Synch one.
-(NSData*) startConnection{
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
while(!isFinished && [[NSRunLoop currentLoop] runMode: NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]){
}
return responseAppData;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
//Code to handle Certificate
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[responseAppData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
isFinished=YES;
}
I also thought of using the while Loop as below, so which one should be used?
while(!isFinished ){
}
Actually it's the opposite. If you want to handle these NSURLConnectionDelegate methods, you need to use asynchronous calls, NOT synchronous. Otherwise the delegates are never called.
typedef void (^onDownload)(NSData *data);
#property (nonatomic,assign) onDownload block;
-(void) startConnectionwithBlock:(onDownload) pBlock;{
self.block = [pBlock copy];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection{
block(self.data);
}
I have a UIWebView, and it would make my life a lot simpler if it could be a delegate for an NSURLConnection. I have made a category like this
#interface UIWebView (NSURLConnectionDelegate) <NSURLConnectionDelegate>
//these methods are used by the NSURLConnection, and are implemented in the .m
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
#end
the methods in the implementation do work, since i had another view that was the delegate before, but now i need to change it because of reasons.
i give the NSURLConnection a delegate like so
#import "UIWebView+NSURLConnectionDelegate.h"
[[NSURLConnection alloc] initWithRequest:request delegate:webview]; //used to be self but now i need the webview to know about its own connection because there are multiple webviews
but none of the delegate methods get called when its a category like this.
has anyone done something like this before or does this not work because the NSURLConnection thinks that webview isnt actually a delegate or something?
edit to show some more code:
- (BOOL)webView:(UIWebView *)webview shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"shouldStartLoadWithRequest %# %d", request.URL.absoluteURL.description, navigationType);
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (!authed) {
authed = NO; //gets set to yes when delegate methods work (also are some print outs in the delegate methods which are not printing at all)
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:webview];
return NO;
}
return YES;
}
This question already has an answer here:
How to identify WHICH NSURLConnection did finish loading when there are multiple ones?
(1 answer)
Closed 9 years ago.
I always used for connect with Server singleton class. I didn't check response from server and easy take data. Now I need use 10 different requests. I create property NSURLConnection. So how can I identify what connections I use in delegate methods like
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
Because from each request I take different data
What you could do is have a custom connection class representing a connection, its data, and optionally some info about the connection. I use this:
#interface MyConnection : NSObject
#property NSURLConnection *connection;
#property id info;
#property NSMutableData *data;
#end
Then just put the connections in an array, and compare the actual NSURLConnection objects to each other, in order to find out which connection received data/failed etc.
Creating a connection:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:someRequest delegate:self];
if (connection){
MyConnection *con = [[MyConnection alloc] init];
con.connection = connection;
con.data = [NSMutableData data];
[self.arrayWithConnections addObject:con];
}
The methods:
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
MyConnection *con = [self getConnection:connection]
[con.data appendData:data];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
MyConnection *con = [self getConnection:connection];
[con.data setLength:0];
}
-(MyConnection *)getConnection:(NSURLConnection *)con
{
for (MyConnection *myCon in self.arrayWithConnections)
if ([con isEqual: myCon.connection])
return myCon;
return nil;
}
I would like to know how I can stop/abort a NSURLConnection while it is performing it load request.
The reason I would like to know is that, I am parsing an XML using NSURLConnection and sometimes the time taken to get a response is too long.
here is my code to make things clearer...
I am parsing an XML using NSXMLParser and loading the req with my soap message before I request it using NSURLConnection
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [[NSMutableData data] retain];
}
}
The following piece of code is what is standard
-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response
{
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection
didReceiveData:(NSData *) data
{
[webData appendData:data];
}
In this, sometimes the time taken for the program to get to connection didReceiveData is too long and the user would need to abort that operation.
So I would like to know if this is possible.
I know how to abort after it starts parsing by using [parser abort] but I dont know how to abort the NSURLConnection.
It would be great if someone could help me out with this.
Use [conn cancel]; to stop an ongoing download.