How to monitor MPMoviePlayerController playback progress while not killing a battery? - ios

I have a media player app that is playing music with MPMoviePlayerController. I need to update the UI based on the playback position. The best I can tell, there is no way to actively receive this info from the player with a callback or something, and I basically need to poll for it myself.
So I thought I would use a simple timer, run every second, for that. The code is such:
Somewhere in the setup code:
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updatePlaybackProgressFromTimer:) userInfo:nil repeats:YES];
And then:
- (void) updatePlaybackProgressFromTimer:(NSTimer *)timer {
if (([UIApplication sharedApplication].applicationState == UIApplicationStateActive) && (player.playbackState == MPMoviePlaybackStatePlaying)) {
CGFloat progress = player.currentPlaybackTime / player.duration;
// do something useful with this info
}
}
The timer is run every second, even when the app is in the background. The method first sees if the app is active and the player is playing, and then does some UI updating.
Is there any battery life implication to running a timer every second in this fashion? Should I be more diligent and try to tear down the timer when entering the background and reactivating it when activating the app? I’m sure there’s some battery life effect, but realistically, how serious is it? Or is there any other recommended ways of doing this kind of thing?

I can't imagine using an NSTimer will significantly impact battery life - unless the work being done when it is triggered impacts battery life. The timer is simply being added to the current run loop:
A timer is not a real-time mechanism; it fires only when one of the
run loop modes to which the timer has been added is running and able
to check if the timer’s firing time has passed.
NSTimer Class Reference
According to the documentation, you should be pausing any timers when your application is about to resign its active status:
In response to this change, your app should do the following in its
applicationWillResignActive: method:
Stop timers and other periodic tasks.
Stop any running metadata queries.
Do not initiate any new tasks.
Pause movie playback (except when playing back over AirPlay).
Enter into a pause state if your app is a game.
Throttle back OpenGL ES frame rates.
Suspend any dispatch queues or operation queues executing non-critical code. (You can continue processing network requests and
other time-sensitive background tasks while inactive.)
When your app is moved back to the active state, its
applicationDidBecomeActive: method should reverse any of the steps
taken in the applicationWillResignActive: method. Thus, upon
reactivation, your app should restart timers, resume dispatch
queues, and throttle up OpenGL ES frame rates again. However, games
should not resume automatically; they should remain paused until the
user chooses to resume them.
iOS App Programming Guide

Related

How to keep a timer counting when app reach background

I prepared a CountDown timer for Pomodoro technique. I would like to know how don't pause the app when it reach a background. I have a method which update UILabel from 20min to 0 by 1sec. When Timer reach 0 it should play the sound and vibrate device. All works fine when app is launched in foreground, but how to do it at background? Is it possible to track timer change when app is in background mode?
BR
iMat
The short answer is no. A timer on a VC will not continue to run when the app is in the background because it goes into suspended mode.
You could schedule a local notification to fire when the app is in the background, but as far as updating the UI label, you'll have to update that when the user comes back into the app.
Invalidate the timer when the app goes to background. Store the remaining time remainingTime and current time backgroundTime. (You can get the current time using Date())
Compare the current time backToForegroundTime when the app comes back with backgroundTime. Subtract them to get the time elapsed timeElapsed.
If timeElapsed is less than the remainingTime, subtract that amount from remainingTime and create the timer again with the new duration.
You can use my approach from this gist. Just create repeating timer and update what ever you want in repeating block, and handle timer finishing in other block on main queue or background queue!
Glad to help with questions!
Apple has defined a specific set of tasks, an app can perform when in background.
Running a timer, unfortunately, is not one of them.
Read Background Execution section of app programming guide for more details.
Most apps, intending to continue to execute code in background, implement one of the allowed long running background modes, even if it is not required for your apps actual functionality, and use them to execute their code.
But be ware, you will be doing something apple specifically asks you not to do. This could result in app store rejection if found.

How to set a timer to run when an app is both in foreground and in background?

I've my app set to keep receiving location updates while in background ("Background modes > Location updates"). My app listens for location updates while in foreground as well. But I need my app to keep listening for locations during a certain number of seconds I'm given, and to show a countdown if the app is in foreground, and to keep the time count if the app goes to background because I need to stop the location manager when the time ends.
I found no problems in setting an NSTimer when the app is in foreground, and showing a countdown, but I don't know how to handle it when app goes to background... in comments in AppDelegate's delegate applicationWillResignActive: and applicationDidEnterBackground: methods it is said that timers should be disabled and invalidated there... in addition, the timer I'm using is created in main thread:
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateCountdown:)
userInfo:nil
repeats:YES];
How could/should I manage this scenario?
Thanks in advance
EDIT: I'm going to try to explain an scenario I could have: my app listens for locations during a given time interval, and then it should stop listening. Let's say I'm going to give the user 5 min to reach certain location. User taps a kind of "start" button, and then I start the location manager to keep track of the user's positions, and I also start a countdown. When the countdown reaches 0, I stop the location manager and check the path the user has followed and the location he has reached. Since the app could go to background after the user has started the countdown, I have enabled Background modes > Location updates. Listening for locations while the app is in background is ok but, what if the countdown finishes while the app is still in background? I need to stop tracking user's positions even if the app is in background. I need a timer to keep running in background whatever countdown I need to set for the user, it could be 5 min, it could be 1 hour...
Thanks again
You can't run a timer in the background forever, it's not one of the background modes allowed by the OS so it will get terminated at some point. Wrapping it in a background task will only buy you a few extra minutes (about 10) at the most.
To properly track time when your app enters the background you need to:
From applicationDidEnterBackground
Save off a time stamp as soon as your app transitions to the background.
Terminate your timers
When your app re-enters the foreground:
From applicationWillEnterForeground edit or 'didUdateLocations'
Read the save time stamp
Use timeIntervalSinceDate to get the lapsed time in seconds
Restart your timer using the time span to set the remaining time
I think this kind of control needs a middleware to operate perfectly. I don't know if your app has backend or not but if it has, you may send a request when user tries to start timer in your app and you do your updates background of your app. After timer finishes backed can send a notification to user about this and you handle it if user comes back to your app or you leave it that way.

iOS UIWebView Javascript Timers

If a page loaded into a UIWebView contains a Javascript setInterval() call, what is the behavior of that timer when the phone goes sleep?
Is there any point in time when all timers are stopped?
Will the timers be restarted when the phone is woken up? If so, does the timer start at where it was paused, or started from 0?
WebKit internal timer management is a little peculiar in general, and in particular in iOS. They are registered in NSDefaultRunLoopMode mode, so for example, when scrolling in UI, Javascript events do not fire, but they are not aggregated either, like with regular timers (NSTimer).
To answer your question, when the application process is suspended, the timers are also suspended, so they will not fire while the process remains suspended. Once the process is resumed for whatever reason (user opened the app, background fetch, etc.), the timers will resume their run, and will resume to the relative point of when the last tick was supposed to be.
So if you set a timer for every 10 seconds, and close the app at t+1 seconds, then open it again at t+35 seconds, the timer will fire after 5 seconds; you would not hear retroactively the t+10, t+20 and t+30 seconds ticks.
Note: This is the in-process model based timer management of UIWebView /WebKitLegacy/; I am not quite experienced with how WKWebView /WebKit2/ handles them.

NSTimer when screen is locked

How do I keep the NSTimer persisting after the screen is locked in iOS 8? Every time I lock the screen it stops. I have tried putting it in an NSRunLoop to no luck.
iOS always suspends NSTimers when the app is backgrounded. You can request some extra background time by using UIApplication beginBackgroundTaskWithExpirationHandler: but be warned that it won't keep your timer going forever. Your best bet is to remember what time you are backgrounded in applicationWillResignActive and then check the current time when you are re-activated in applicationDidBecomeActive or applicationWillEnterForeground. Then you can calculate how much time passed when your app was inactive and do whatever is appropriate.
See the UIApplication docs here https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html, particularly the section on "Managing Background Execution".

stop audio while in background

There is an app called IHeartRadio which lets you set a sleep timer which will shut off audio after a specified interval.
You first choose a station to listen to and then select an amount of time to sleep after which the radio station will stop playing. The app does not need to be in the foreground for this to happen.
How is it possible for an app to stop audio while it is in the background?
One theory was to set a UILocalNotification with silent audio. Then the UILocalNotification would take over the audio of the device, in effect silencing whatever audio was playing. That didn't work.
Timers don't work in the background, which doesn't leave much in terms of time-based behavior in the background.
When the UIBackgroundModes key contains the audio value, the system’s media frameworks automatically prevent the corresponding app from being suspended when it moves to the background. As long as it is playing audio or video content, the app continues to run in the background. However, if the app stops playing the audio or video, the system suspends it.
From iOS App Programming Guide.
You can use applicationDidEnterBackground event in your AppDelegate to handle application going to background and stop audio in this method.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
// add your audio stopping code ..
}

Resources