http://stackoverflow.com/questions/12071726/how-to-use-beginbackgroundtaskwithexpirationhandler-for-already-running-task-in
http://stackoverflow.com/questions/28993628/how-to-pass-background-task-identifier-to-completion-handler
http://www.raywenderlich.com/29948/backgrounding-for-ios
The Problem
When you do an update, an add of an event, or whatever feature that you would like to update on the Azure, it will take anywhere from 1 to maybe a few long seconds to sync the data over the network.
This will cause a problem if the user hits the home button and causes our app to process in the background. iOS will give the app 5 seconds to do what it needs to do before it suspends it.
But what if our network task takes longer!? How to make sure whatever network task we have runs completes safely?
Solution
Apple introduced a feature where you get to background your task for a full 3 minutes. If it can’t finish after 3 minutes, then you’re out of luck and it will suspend your app. But for some network task where we’re synching user data/actions against an Azure server, etc…3 minutes is plenty!
How it Works
When a network task happens, we create a timer to check on the application state for the lifetime of that network task.
For example, when the network starts, we create a timer that calls on method evaluate every 1 second to check on the application state like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(evaluate) userInfo:nil repeats:YES]; //set expiration handler ... ... ... - (void)evaluate { NSLog(@"do your network process"); //this gets checked every 0.5 seconds to see whether we are in UIApplicationStateActive or not //if we are in active state, do nothing..just process if (UIApplication.sharedApplication.applicationState == UIApplicationStateActive) { NSLog(@"APP STAT: ACTIVE"); } //however, if the user pushes the Home button, and we get backgrounded, it shows how much time is remaining else //UIApplicationStateInactive, or UIApplicationStateBackground { NSLog(@"APP STATE: BACKGROUNDED"); NSLog(@"Background time remaining = %.1f seconds", [UIApplication sharedApplication].backgroundTimeRemaining); } } |
For the whole duration of the network task, we need to check if the APP is in active state UIApplicationStateActive, or not. If its in the active state, then do nothing, and let the app continue its network task.
Say while the network task is processing, the user presses the Home button. Our app gets backgrounded, and the (UIApplicationStateActive) STATE would the be false. When this happens, the App’s singleton backgroundTimeRemaining will start counting down.
1 |
[UIApplication sharedApplication].backgroundTimeRemaining |
We simply log out to see what the remaining time is. We have a full 180 seconds.
The logs will let you know that as you’re processing this network task, the backgroundTimeRemaining will decrease while the app is in the background.
If the user clicks on the app again within the 3 minutes, your backgroundTimeRemaining will reset.
When the 3 minutes are up, it will call an expiration handler that your property
1 |
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTask; |
points to like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"Initially, we set self.backgroundTask to UIBackgroundTaskInvalid"); self.backgroundTask = UIBackgroundTaskInvalid; } .... ... // sets up timer to evaluate method // you designate an expiration handler to run when the 3 minutes are up NSLog(@"set self.backgroundTask to UIApplication singleton's beginBackgroundTaskWithExpirationHandler's block method"); self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ NSLog(@"‡‡‡ Background handler called. Not running background tasks anymore: YOUR 3 MINUTES ARE UP! ‡‡‡"); [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask]; NSLog(@"endBackgroundTask called, property backgroundTask takes on UIBackgroundTaskInvalid"); self.backgroundTask = UIBackgroundTaskInvalid; }]; |