Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>So it turns out I had two weird things going on that were tripping up the background task:</p> <ul> <li>Asynchronous URL connections (when their initiating method finished, iOS thought the background task was done even if the response hadn't yet been received)</li> <li>A location manager specific to the background task (apparently a major no-no...apple's got some documentation on this but the console would spit out an error about it sometimes)</li> </ul> <p>Here's the code I'm now using (it works so far):</p> <pre><code> __block UIBackgroundTaskIdentifier bgTask; UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance NSLog(@"BackgroundTimeRemaining before block: %f", application.backgroundTimeRemaining); bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ // Clean up any unfinished task business by marking where you. // stopped or ending the task outright. [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; // Start the long-running task and return immediately. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Do the work associated with the task, preferably in chunks. NSLog(@"BackgroundTimeRemaining after block: %f", application.backgroundTimeRemaining); //Create secondary managed object context for new thread NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init]; [backgroundContext setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]]; /* Save the background context and handle the save notification */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; //Set a grace period during which background updates can't stack up... //This number should be more than the longest combo of timeout values in adjustDataInBackground NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:90]; __lastBackgroundSnapshot = stopDate; NSLog(@"Stop time = %@", stopDate); MasterViewController *masterViewContoller = [[MasterViewController alloc] init]; masterViewContoller.managedObjectContext = backgroundContext; NSLog(@"Successfully fired up masterViewController class"); [masterViewContoller adjustDataInBackground]; NSLog(@"adjustDataInBackground!"); //just in case [[self locationManager] stopUpdatingLocation]; //save background context [backgroundContext save:NULL]; NSLog(@"Uploading in background"); //send results to server postToServer *uploadService = [[postToServer alloc] init]; uploadService.managedObjectContext = backgroundContext; [uploadService uploadToServer]; //save background context after objects are marked as uploaded [backgroundContext save:NULL]; //unregister self for notifications [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }); </code></pre> <p>In addition, I added the following runloop to my asynchronous URLConnection objects so they stayed alive long enough to finish their business. While it's not the most graceful way of handling it, it works as long as you can handle the failure gracefully if the runloop ends without the server exchange finishing.</p> <p>A runloop (adjusted for different timeouts depending on the task):</p> <pre><code>//marks the attempt as beginning self.doneUpload = [NSNumber numberWithBool:FALSE]; [[uploadAttempt alloc] fireTheUploadMethod]; //if uploading in the background, initiate a runloop to keep this object alive until it times out or finishes if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { //Timeout length to wait in seconds to allow for async background execution NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:120]; do { NSLog(@"Waiting for upload to return, time left before timeout: %f", [stopDate timeIntervalSinceNow]); [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; } while ([stopDate timeIntervalSinceNow] &gt; 0 &amp;&amp; self.doneUpload == [NSNumber numberWithBool:FALSE]); } </code></pre> <p>Hope this helps anyone who runs into this in the future!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload