iPhone SDK: URL request not timing out - iphone

I am having a problem with a network request that should timeout, but the method is not called. The request is as follows:
#define kCONNECT_TIMEOUT 20.0
request = [NSMutableURLRequest requestWithURL: aUrl];
[request setHTTPMethod: #"POST"];
postData = [jsonData dataUsingEncoding:NSASCIIStringEncoding];
[request setHTTPBody:postData];
[request setValue:#"text/xml" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setCachePolicy:NSURLCacheStorageAllowed];
[request setTimeoutInterval:kCONNECT_TIMEOUT];
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
assert(self.connection != nil);
This should get a callback to
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)_error
But after 4 minutes no error message is displayed. Anyone know why this might be?

If you want better timeout management on http requests using NSURLConnection then it is much better to run the request asynchronous together with an NSTimer that can cancel the NSURLConnection when it fires because the timeout period expired.
This also means you don't have to deal with threads, which is generally a good idea. Async event (runloop) based operations are the way to go in 99.9% of the cases on the iPhone.

A representative from Apple has divulged that SDK 3.0 and later enforce a minimum timeout of (you guessed it) four minutes:
https://devforums.apple.com/thread/25282
If you try to set a timeout value of less than 240 seconds, it gets clamped up to 240. If you need a shorter timeout, I cast my vote for St3fan's solution.

Related

ASIHTTPRequest timeout not working

The code is the following, however I can't figure out why it's not timing out even after 10 seconds has passed. Any idea?
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:sourceURL];
[request setTimeOutSeconds:5.0];
[request setDelegate:self];
[request setCompletionBlock:^{
//some code
}];
[request setFailedBlock:^{
//some code
}];
self.currentRequest_ = request;
[self.currentRequest_ startAsynchronous];
- (void)requestFailed:(ASIHTTPRequest *)request {
NSLog(#"FAILED");
}
Perhaps the request completed successfully then?
Other possibility is that there was data being received at least every 5 seconds, but the full data has not yet been received. ASI will only timeout if nothing is received for the timeout period, so if data is constantly arrived the request won't time out.
I don't think you call -[ ASIHTTPRequest startAsynchronous]... Instead add the request (which is a subclass of NSOperation) to a ASINetworkQueue (a subclass of NSOperationQueue). HTH

iPhone App crashes on http post using asi-http-request

I am using asi-http-request to upload a file to server.
My code used to work OK, until at some point it started crashing.
The crash happens in 2 ways:
- The file is being uploaded properly and the progress is working OK, until is reaches to the end and then the entire app crash.
- When user press the "Cancel" button in order to cancel the upload.
I get this error on the console:
terminate called after throwing an instance of 'NSException'
and: Thread 1: Program received signal: SIGABRT
These are the 2 errors I see. No more information.
This is the code:
request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:UPLOAD_URL_DEV]];
[request setDelegate:self];
[request setFile:videoFile forKey:#"video"];
[request setPostValue:longitude forKey:#"longitude"];
[request setPostValue:latitude forKey:#"latitude"];
[request setPostValue:horizontalAccuracy forKey:#"accuracytHorizontal"];
[request setPostValue:verticalAccuracy forKey:#"accuracyVertical"];
[request setPostValue:context forKey:#"context"];
[request setPostValue:[UIDevice currentDevice].uniqueIdentifier forKey:#"deviceId"];
NSLog([NSString stringWithFormat:#"Upload Recording time: %#", currentTime]);
[request setPostValue:currentTime forKey:#"time"];
NSLog([NSString stringWithFormat:#"Facebook access token: %#", facebook.accessToken]);
[request setPostValue:facebook.accessToken forKey:#"accessToken"];
NSLog([NSString stringWithFormat:#"Facebook user id: %#", [defaults objectForKey:#"facebook_user_id"]]);
[request setPostValue:[defaults objectForKey:#"facebook_user_id"] forKey:#"userId"];
[request setUploadProgressDelegate:uploadProgress];
uploadProgress.progress = 0;
uploadProgress.hidden = NO;
labelSendVideo.hidden = NO;
NSLog(#"Starting async upload");
[request startAsynchronous];
Can anyone tell me what is wrong? Maybe it is a memory issue?
There is no code for the "didFinish..." and "didFail..." delegate methods.
Also make sure that the delegate instance (the "self" here) is not getting deallocated before the download is finished or cancelled.

Delaying, cancelling or replacing a ASIFormDataRequest in a NSOperationQueue (for a searchbox)

I'm successfully making a ASIFormDataRequest using the below code.
//get groups
if (![self queue]) {
[self setQueue:[[[NSOperationQueue alloc] init] autorelease]];
}
//make the url by appending the URL from the Constant class to the jsp name
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", URL, #"connectors/searchGroupsServlet.jsp"]];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request addRequestHeader:#"User-Agent" value:USER_AGENT];
[request addPostValue:[login username] forKey:#"username"];
[request addPostValue:[login password] forKey:#"password"];
[request addPostValue:[searchText lowercaseString] forKey:#"query"];
[request addPostValue:GROUP_FILTER_LIMIT forKey:#"limit"];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
This request is currently made on every key press a user makes in a searchbox (The text typed is sent off in the request as the search string). However, rather than sending the request on every key press, I want to delay the request by a second to allow users to type further characters into the searchbox before the request is sent.
I've successfully made a thread that waits a second as users continue to type (although admittedly Im not convinced this is the best way to do it yet, but it works for now)...
this
[self performSelectorInBackground:#selector(wait:) withObject:request];
calls this
-(void)wait:(NSString *)request
{
[NSThread sleepForTimeInterval:1.00];
[[self queue] addOperation:request]; //queue is an NSOperationQueue
}
but, if a user continues to type, I haven't managed to work out how to cancel the request or not put the request in the queue, or empty the queue and replace it with the new request.
Finally, obviously I could force users to wait until they have pressed the 'search' button on the pop-up keyboard, but I was hoping to provide search results without that.
Thanks
The answer was to create an NSTimer, and invalidate it whenever a new key press had been made. Then start it again.
[timer invalidate];
You can try this to cancel
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument

iOS Application Background Downloading

Hey! I need to know how I can have my iOS Application start a download in the background of the application (like, have the download run in the AppDelegate file) so changing ViewControllers will not interrupt or cancel the download. I also need to be able to get the progress of the download (0.00000 - 1.00000), to set a UIProgressView object to, which also means I need a - (void)progressDidChangeTo:(int)progress function.
Just use ASIHTTPRequest it is way easier than NSURLRequest and does exactly what you need.
It examples that shows how to download in background and how to report progress.
I wouldn't download anything in the AppDelegate directly. Instead I would create a separated class just for that purpose. Let's call it MyService I would then initialize that class in my app delegate.
The class can work as a singleton or can be passed to each view controller that requires it.
In MyService class I would add the ASINetworkQueue and few methods to handle the requests when they are ready. Here is the code from ASI examples that you can use:
- (IBAction)startBackgroundDownloading:(id)sender
{
if (!self.queue) {
self.queue = [[[ASINetworkQueue alloc] init] autorelease];
}
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
[self.queue addOperation:request]; //queue is an NSOperationQueue
[self.queue go];
}
- (void)requestDone:(ASIHTTPRequest *)request
{
NSString *response = [request responseString];
//Do something useful with the content of that request.
}
- (void)requestWentWrong:(ASIHTTPRequest *)request
{
NSError *error = [request error];
}
If you need to set the progress bar. I would just expose the setDownloadProgressDelegate of ASINetworkQueue in my MyService class and set it in my ViewControllers like that:
[[MyService service] setDownloadProgressDelegate: self.myUIProgressView];
BTW. If you need to continue downloading even when your app exits you can set ShouldContinueWhenAppEntersBackground property of your request to YES.
you can use NSURLConnection to start an asynchronous request that won't cause your UI to be frozen. You can do it by doing something like:
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url];
connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
[urlRequest release];
in order to have your progress you can use the:
connection:didReceiveResponse:(NSURLResponse *)response;
delegate call to inspect the response.expectedContentLength and then use the
connection:didReceiveData:(NSData *)data
to track the amount of data that was downloaded and calculate a percentage.
Hope this helps,
Moszi

cancel a SynchronousRequest in iPhone SDK. (TIMEOUT Interval not working)

I've queried this forum for hours looking for an idea/answer/solution for my problem, but came up empty every time.
i have created a SynchronousRequest using the following:
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] initWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [params length]];
[theRequest addValue: msgLength forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setTimeoutInterval:3.0];
[theRequest setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
[theRequest setHTTPBody: [params dataUsingEncoding:NSUTF8StringEncoding]];
NSData *aData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];
the connection is established, and the data is retrieved successfully to aData.
but, when there is a connection problem, or the server is not available, the request is attempting to connect for 75 seconds which is tooooo much time for the timeout interval,
i have added the setTimeoutInterval parameter (with 3 seconds) but it does not affect the connection,
i saw some answers from people saying that i should use NSTimer, and runLoop,
but it's not clear to me how this should be implemented.
PLEASE HELP!
the users are waiting 75 seconds before they get a timeout error message! it's ridiculous
appreciate your help.
On the iPhone a minimum timeout interval is hard-coded into the framework, you can't set the timeout below 75 seconds. Apple did this because there's frequently a significant amount of lag when you're dealing with cellular data connections.
What you want to do in most situations use an asynchronous network connection (so that your GUI doesn't freeze) and allow the request to go the full 75 seconds before timing out.
Read Apple's instructions for how to set up an asynchronous connection, it's a great start.
If you really do want to set a very short timeout, you can use an NSTimer like this:
- (void)loadURL:(NSURL *)url {
/* Set up the NSURLConnection here */
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(cancelURLConnection:) userInfo:nil repeats:NO];
}
- (void)cancelURLConnection:(NSTimer)timer {
[self.connection cancel]
}
I'm not at my desktop, so that code may be buggy and it's definitely incomplete. Also note that you can't easily use a timer to kill a synchronous web requset, since the synchronous request blocks the runloop and the timer won't fire until the request is done.
may I suggest having a look at the sample code from simpleURLconnections?
From that code, the NSMutableURLRequest is sent using
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
for both retrieving and sending data (but have a look at the rest of the code). Maybe the problem lies in the sendSynchronousRequest and you can avoid using that ?
Regards
You could use some code like the following (taken from an app I'm working on) - isFinished is a global variable:
- (void)someMethod {
[[WSXMLRPCController sharedInstance] validateLicenseWithServiceURL:serviceUrl username:username password:password delegate:self];
isFinished = NO;
NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:10]; // break the loop after 10 seconds and not finished with the request from the call above ...
while(!isFinished && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate]){
if([endDate compare:[NSDate date]] == NSOrderedAscending){
[self connection:nil didFailWithError:nil forMethod:nil];
}
}
}
- (void)connection: (XMLRPCConnection *)connection didFailWithError: (NSError *)error forMethod: (NSString *)method {
isFinished = YES;
}
- (void)connection: (XMLRPCConnection *)connection didReceiveResponse: (XMLRPCResponse *)response forMethod: (NSString *)method {
isFinished = YES;
}
Probably not the cleanest solution, but it works. BTW this code is making use of the WordPress XMLRPCConnection class and delegate methods, but the same if possible with the NSURLConnection class and delegate methods.