NSURLConnection hangs for server which is not available - iphone

I'm using NSURLConnection initWithRequest to get some data from a server. This works fine when the server is available. However when the server is not available my app hangs and becomes totally unresponsive for at least 40-50 seconds. I've tried using a timeoutInterval, as well as a timer to cancel the request. However my app still hangs.
Whilst my app is hanging, none of the NSURLConnectionDelegate methods have been called. The onTimeExpire gets called but doesn't do anything. Once the app becomes responsive again (50 seconds later...), the NSURLConnectionDelegate delegate methods get called and all is good...
The server is a local server with ip 192.168.x.x which will pull data down to the app only when the server (and csv) file is available.
I thought of doing a simple check before firing off the NSURLConnection to see if the server is online first. But can't seem to work out how to do this? Any ideas?
-(id) loadCSVByURL:(NSString *)urlString
{
// Create the request.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0f];
self.timer = [NSTimer scheduledTimerWithTimeInterval:20 //for testing..
target:self
selector:#selector(onTimeExpired)
userInfo:nil
repeats:NO];
(void)[self.connection initWithRequest:request delegate:self];
//THE APP HANGS HERE!!!
return self;
}
-(void)onTimeExpired
{
NSLog(#"cancelling connection now!");
[self.connection cancel];
}

You are setting a timeout of 20 but a connection timeout of 30. That means that even if your setup were correct, the timer would fire before the unsuccessful connection fails.
More importantly, you are sending an init message to your connection object twice. This does not make sense.
You need instead to create the connection with the request and then start it.
self.connection = [[NSURLConnection alloc]
initWithRequest:request delegate:self];
[connection start];
You then react to the failure of the connection in the NSURLConnectionDelegate callback connection:didFailWithError: which should fire after the connection times out.

Related

ASIHTTPRequest in ASINetworkQueue: Cancel request while queue is running

I am using ASIHTTPRequest (I know, I know, it is not being maintained - that doesn't matter; it works and it is what my app is built on) to run a series of HTTP requests through ASINetworkQueue.
The problem is that my queue will have many requests (thousands), and it will take a while to run. In the time that it is running, some of the data in the app may have changed which would make some of the request unnecessary. I would like to run a validation method on each request right before it runs, and if the validation method does not check out, then it will cancel that request and go on to the next one.
Right now, I am using the following code to create my ASIHTTPRequests:
ASINetworkQueue *myQueue = [ASINetworkQueue queue];
NSURL *URL = [NSURL URLWithString:#"http://mywebsite.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setStartedBlock:^{
NSLog(#"Request started: %#", request.url.absoluteString);
}];
[request setCompletionBlock:^{
NSLog(#"Request completed: %#", request.url.absoluteString);
// do some code here to clean up since it's finished
}];
[myQueue addOperation:request];
My current thinking is to put something into the startedBlock, so it would do:
[request setStartedBlock:^{
NSLog(#"Request started");
if (![self myValidationMethod]) {
[request cancel]; // <----------
}
}];
However when I do this, I get the following warning from Xcode:
"Capturing 'request' strongly in this block is likely to lead to a retain cycle."
First, is this the right method to go about doing this? I can't seem to find a way to remove a specific ASIHTTPRequest from an ASINetworkQueue. Second, is this warning from Xcode something that I will need to worry about?
About the warning you capture the blocks 'container' and form a cycle... just say:
__weak ASIHTTPRequest *rw = request;
and use that in the block.
as for the started block approach. doesnt sound perfect to me but I dont know a better approach...

NSURLRequest delegates not getting called on Device Works fine on simulator.

NSURLRequest delegate methods are not getting called when i run the application on the device. It works perfectly on the simulator though. Also its not the case of my view loading before the request is fulfilled because i enable to view to be loaded only once the connection has received the data.
My code requesting url is here. Any help greatly appreciated.
- (void)viewWillAppear:(BOOL)animated
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://-dev01x/content"]; cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60];
[request setHTTPMethod:#"GET"];
_getData = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"HELLO %#",_getData);
}
Your code as provided here makes no sense:
_getData = [[NSURLConnection alloc] initWithRequest:request delegate:self];
That line returns a pointer to a NSURLConnection object, your variable name is misleading. If you want to block at this point (and does that even work) then it would appear you need to use sendSynchronousRequest at that point.
I do something similar to what you want to do, but in a more traditional way. In viewDidLoad or even in the initWithFrame, I will start up an asynchronous connection, set a flag, and set the view backgroundColor to black or white (I use a spinner too normally). When I get viewWillAppear, if the connection has completed, all is well and I set the various UI elements. If not, then don't do anything, and later, when the connection completes, pull down the spinner and update the UI.
The only way I can think of to block the main thread at the point you are trying to would be to use that synchronous request (which IMHO is a really bad way to deal with this).
The beauty of doing things in the background is that if its taking too long, the user can tap the back button or go somewhere else in the app [in which case you cancel the connection and tear everything down.]
Check url u provide to NSMutableURLRequest when checked in browser gives nothing
NSMutableURLRequest needs valid url

Failing to connect to my WCF webget exposed service from objective c

This is a tough question.
When I press on the button, it is supposed to connect to a URL, which will initiate that method of my wcf service. However, when I debug, I noticed that the connection fails (like none of the delegate methods are called and stuff). I know that my WCF service works because when i type the URL in safari directly, it works perfectly, and performs that method. Here is my code:
- (IBAction)RBCButtonPressed:(id)sender {
//[[UIApplication sharedApplication] openURL:[NSURL URLWithString: #"http://10.1.51.55:8732/windows2/OnApplication?appName=RoyalBank.BankOfTheFuture.Surface.exe&directory=C:\\Program Files (x86)\\Bank of the Future"]];
NSString *urlString = #"http://10.1.51.55:8732/windows2/OnApplication?appName=RoyalBank.BankOfTheFuture.Surface.exe&directory=C:\\Program Files (x86)\\Bank of the Future";
NSLog(urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url ];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
if(connection)
self.valueReturned = [[NSData data] retain];
else
NSLog(#"data failed");
NSLog(#"connection failed");
[captureView addSubview:loader];
}
I'm not including the delegate methods cause they aren't called anyway.
It prints "connection failed."
UPDATE: When I use a method that takes in only one parameter, it works fine from both the browser and the device (the connection succeeds and the delegate methods are called). However, when there are two parameters, it works fine only from the browser. The connection always fails.
Possibilities - the use of the backslash confuses it somehow (?).
This is urgent and any help would be greatly appreciated.
EDIT: It will always print "connection failed" I realize, but I know that the connection fails because 1) the delegate methods aren't called and 2)the application does not turn on, which it will if the method is called.
If you check your NSURL object in the debugger, you'll find it's nil immediately after you try to initialize it. This is because it's malformed. Try URL encoding it first.
Notice the URL you pasted into safari changes to this:
http://10.1.51.55:8732/windows2/OnApplication?appName=RoyalBank.BankOfTheFuture.Surface.exe&directory=C:%5C%5CProgram%20Files%20(x86)%5C%5CBank%20of%20the%20Future
See all the %20s and %5Cs, etc.? That's because safari URL encodes it before sending the request. You must do the same.
Best regards.
It's failing because you are releasing connection right after you create it. If you're not using ARC, you could create it with autorelease, or just use ARC and forget about the releases.

Asynchronous NSURLConnection on separate thread fails to call delegate methods

I am running a NSURLConnection on a separate thread (I am aware that it is asynchronous and works when running on the main thread), but it is not making delegate calls even when I pass the parent thread as the delegate. Does anyone know how to do this?
Code:
-(void)startConnectionWithUrlPath:(NSString*)URLpath {
//initiates the download connection - setup
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
//initiates the download connection on a seperate thread
[NSThread detachNewThreadSelector:#selector(startDownloading:) toTarget:self withObject:self];
}
-(void)startDownloading:(id)parentThread {
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
[NSURLConnection connectionWithRequest:myURLRequest delegate:parentThread];
//The delegate methods are setup in the rest of the class but they are never getting called...
[pool drain];
}
EDIT*
The reason I need to run NSURLConnection on a separate thread is because I am downloading something in my iPhone app and the download cancels when the user locks the screen (it continues fine if the user simply presses the home button and the app goes into the background). I understand this is due to my running the connection asynchronously on the main thread and not a separate one.
I have also tried this code (NOT in a separate thread) when initiating the NSURLConnection:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:myURLRequest delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
[connection release];
But it I have the same problem with this regarding the download being cancelled on screen lock.
*UPDATE
To add to Thomas' answer below (Please note that James Webster's answer is also correct regarding the exiting of a thread) the Apple docs explain:
"Suspended state - The app is in the background but is not executing code. The system moves apps to this state automatically and does not notify them before doing so. While suspended, an app remains in memory but does not execute any code."
Since when the screen is locked by the user the app is put into the background state and than right away into the suspended state, all execution is stopped killing any downloads and no warning that this is about to happen is given... there may be a notification which tells me that the user has locked the screen but I haven't found one yet.
I therefore pause (save certain information and cancel the NSURLConnection) all downloads when the app goes into the background and resume it with the HTTP Range header when it gets active again.
This is a workaround which is ok but not ideal since the download is not occurring in the background which affects the user experience negatively... bummer.
Since your NSURLConnection is asynchronous, the end of your -startDownloading method is reached immediately, and the thread exits.
You should indeed schedule your connection on the main runloop (or use GCD).
The device lock is another issue. When the device is locked, your application is suspended to save battery life. You can probably ask for an extra amount of time when suspending in order to finish your download.
I think your problem might be that the NSURLConnection has been deallocated as soon as you exit the startDownloading: message (or more accurately when your autorelease pool is drained)
However I think your methodology might be a bit uncouth anyway. NSURLConnection the way you are using it is asynchronous and will appear to be threaded anyway.
Try this and see if it works as you expect it to (i.e. your app doesn't pause while your connection is busy)
-(void)startConnectionWithUrlPath:(NSString*)URLpath {
//initiates the download connection - setup
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
[NSURLConnection connectionWithRequest:myURLRequest delegate:self];
}

NSURLConnection and NSTIMER issue

I am currently trying to have a time out of 20 second when making an async request.
The issue am having is that the NSURLconnection runs on the main thread and therefore, if I run an NSTIMER to count the number of seconds that has passed, it never fires the selector since the NSURLconnection is blocking the main thread. I can probably run the NSURLconnection on a different thread since it is thread safe but I have weird issues with my delegates not being called etc.. any help is appreciated.
Below is my sniplet:
NSURL *requestURL = [NSURL URLWithString:SERVER];
NSMutableURLRequest *req = [[[NSMutableURLRequest alloc] initWithURL:requestURL] autorelease];
theConnection = [[NSURLConnection alloc]initWithRequest:req delegate:self];
if (!timeoutTimer) {
NSLog(#"Create timer");
timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:TIMEOUT target:self selector:#selector(cancelURLConnection) userInfo:nil repeats:YES];
}
The asynchronous methods of NSURLConnection do not block the main thread. If your timer isn't firing, this has other reasons. Your problems with using it on a background thread result from the fact that a background thread doesn't have a runloop by default.
This is a great tutorial on how to set up a simple NSOperation to run a method on a separate thread. I'd start with this based on what you have mentioned. Hope that helps!