Unable to download data in Background after 10 minutes? - ios5

I am working with the app in which i want to download data in background. As per Apple multitasking guidelines, you are allowed to download data for 10 minutes.
In my case, it will take more than 10 minutes for downloading file and downloading get failed.
Initial downloading request is from DownloadViewController as below.
- (IBAction)performLargeUpload:(id)sender {
[request cancel];
[self setRequest:[ASIFormDataRequest requestWithURL:[NSURL URLWithString:#"http://mirrorblender.top-ix.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_stereo.avi"]]]; // 149MB
[request setDownloadDestinationPath:[[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:#"big_buck_bunny_480p_stereo.avi"]];
[request setTimeOutSeconds:20];
[request setDownloadProgressDelegate:progressIndicator];
[request setUserInfo:[NSDictionary dictionaryWithObject:#"request1" forKey:#"name"]];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
[request setShouldContinueWhenAppEntersBackground:YES];
#endif
[request setDelegate:self];
[request setDidFailSelector:#selector(uploadFailed:)];
[request setDidFinishSelector:#selector(uploadFinished:)];
[request setAllowResumeForFileDownloads:YES];
[request startAsynchronous];
[resultView setText:#"Downloading data..."];
}
In appDelegate, i have put this code When applicationDidEnterBackground
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
backgroundTimer=nil;
backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(keepAlive) userInfo:nil repeats:YES];
}
How to extend the time for background downloading??

You can't do it. Stop trying.

Yes this can't be done, I'll suggest when application goes background if any download connection exists
better to cancel it & start the download again when application comes foreground. Other possibility we need to maintain a pause & resume, This required some server changes also.
This may helpful:
How to pause/resume downloads in iPhone (iOS)

It is possible to download assets when your application is not active only and only if your app has periodically updated content like magazines and newspapers. In this case you can use the Newsstand framework.

Related

iOS background Location not sending http request

My app needs to track the users location in the background but it is failing to send a 'get' request. The http request gets sent immediately when the app comes to the foreground. I am using RestKit for all my network requests and I followed this tutorial to setup my background locations service.
In my applicationDidEnterBackground
-(void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgLocationManager = [[CLLocationManager alloc] init];
self.bgLocationManager.delegate = self;
[self.bgLocationManager startMonitoringSignificantLocationChanges];
NSLog(#"Entered Background");
}
and I stopMonitoringSignificantLocationChange in my applicationDidBecomeActive delegate
This is my locationManager delegate where I accept the new updated location and send to my server
-(void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"I am in the background");
bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK
NSString *currentLatitude = [[NSString alloc]
initWithFormat:#"%g",
newLocation.coordinate.latitude];
NSString *currentLongitude = [[NSString alloc]
initWithFormat:#"%g",
newLocation.coordinate.longitude];
NSString *webToken = [[NSUserDefaults standardUserDefaults] stringForKey:#"userWebToken"];
NSLog(#"I am in the bgTask, my lat %#", currentLatitude);
NSDictionary *queryParams;
queryParams = [NSDictionary dictionaryWithObjectsAndKeys:webToken, #"auth_token", currentLongitude, #"lng", currentLatitude, #"lat", nil];
RKRequest* request = [[RKClient sharedClient] post:#"/api/locations/background_update" params:queryParams delegate:self];
//default is RKRequestBackgroundPolicyNone
request.backgroundPolicy = RKRequestBackgroundPolicyContinue;
// AFTER ALL THE UPDATES, close the task
if (bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
The network requests works as planned but it will not get called in the background. Is there any additional steps I need? In my info.plist I have the Required Background modes key and location-services as the value.
EDIT
I also referred to this past SO answer. I ran some tests with putting logs throughout the didUpdateToLocation call and they were all called but the 'get' request was not sent. Instead when I finally launch the app to the foreground it sent all the built of network requests (over 10).
EDIT (2)
I added RKRequestBackgroundPolicyContinue to my request but it did not change my results. (As you can see here in the background upload/download for restkit). I see Restkit initialize the host but fails to send the request until the app becomes active.
ANSWER
RestKit must be doing something that is prohibited in the background. Using an NSURLRequest works perfectly.
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.example.com/api/locations/background_update"]];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:jsonData];
NSHTTPURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
It is fine to use a synchronous request since there is no UI to disrupt with background tasks
Re-creating original suggestion as an answer
Have your try replacing your restKit calls with a stock synchronous NSURLConnection? – dklt Sep 20
I'm using exactly the same code as you and it works for me in RestKit. The only way I could make it work is ny creating a synchronous request (it doesn't make a lot of sense to do it asynchronously in this context anyway!). Please check this code and let us know if it works:
// REMEMBER. We are running in the background if this is being executed.
// We can't assume normal network access.
// bgTask is defined as an instance variable of type UIBackgroundTaskIdentifier
// Note that the expiration handler block simply ends the task. It is important that we always
// end tasks that we have started.
_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:_bgTask];
}];
// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK
// For example, I can do a series of SYNCHRONOUS network methods (we're in the background, there is
// no UI to block so synchronous is the correct approach here).
NSNumber *latNumber = [NSNumber numberWithDouble:location.coordinate.latitude];
NSNumber *lngNumber = [NSNumber numberWithDouble:location.coordinate.longitude];
NSNumber *accuracyNumber = [NSNumber numberWithDouble:location.horizontalAccuracy];
NSDictionary *params = [NSDictionary dictionaryWithKeysAndObjects:#"lat",latNumber,#"lng",lngNumber,#"accuracy",accuracyNumber, nil];
RKURL *URL = [RKURL URLWithBaseURL:[NSURL URLWithString:SERVER_URL] resourcePath:#"/user/location/update" queryParameters:params];
RKRequest *request = [RKRequest requestWithURL:URL];
request.method = RKRequestMethodGET;
NSLog(#"Sending location to the server");
RKResponse *response = [request sendSynchronously];
if (response.isFailure)
NSLog(#"Unable to send background location, failure: %#", response.failureErrorDescription);
else {
NSError *error = nil;
NSDictionary *parsedBody = [response parsedBody:&error];
if (YES == [[parsedBody objectForKey:#"result"] boolValue]){
NSLog(#"Background location sent to server");
}
else {
//Something went bad
NSLog(#"Failed to send background location");
}
}
// AFTER ALL THE UPDATES, close the task
if (_bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:_bgTask];
_bgTask = UIBackgroundTaskInvalid;
}
I'm almost sure the new thread spawned for your RKClient request is automatically killed after invoking it.
When you're application is running in the background you can finish a HTTP request you started before you entered the background but you cannot initiate a new request. You can only initiate certain network operations while in the background (voip, newsstand).

NSThread is blocking my GUI

I use a NSThread in order to download videos and images from a server side.It work looks and works great except the fact that when the downloading is done my GUI gets blocked until the download is complete.When the download is finished it takes a few seconds to work again.
this is how the server request is done:
- (void) repeatRequest{
NSLog(#"repeatRequest");
[NSThread detachNewThreadSelector:#selector(backgroundRequest) toTarget:self withObject:nil];
}
- (void) backgroundRequest{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:myURLStr];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
[pool drain];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
//do things
}
IMPORTANTAnd I also tried to start the ASIHTTPRequest from the GUI thread but with the same behaviour.
Any idea about what could be wrong?
EDIT:
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
//internetReachable = [[Reachability reachabilityForInternetConnection] retain];
if(timer1 == nil)
{
timer1 = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector: #selector(repeatRequest) userInfo: nil repeats: YES];
}
}
Try to run synchronous ASIHTTPRequest in your background thread, and handle results not in delegate method (requestFinished), but after [request startSynchronous];
I don't know anything about ASIHTTPRequest but i would assume its -startAsynchronous method already handles the background downloading for you. It all likelihood, it is returning immediately and your new thread is exiting. Also, you should just use [pool release] at the end of a thread method instead of [pool drain], it will be drained upon release, and you won't be leaking an NSAutoReleasePool. Does ASIHTTPRequest have a -startSynchronous (or just plain -start) method? Try using that within -backgroundRequest, as it should block the premature exit of that thread.

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.

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.