Detect when app is minimized (iOS) - ios

I am building a mobile game and I have a NSTimer for my stopwatch. Everything works fine, except when I minimize the app (press home screen), or when it is interrupted by, say a phone call, the NSTimer continues running in the background, when the application is not in use.
I need to invalidate this timer when the app is minimized/interrupted and create a new timer when the app is resumed. What methods handle when the app is minimized and resumed?

You can invalidate timer in AppDelegate in UIApplication delegate method applicationDidEnterBackground this way,
- (void)applicationDidEnterBackground:(UIApplication *)application {
[timer invalidate];
timer = nil;
}
And create a new timer in method applicationWillEnterForeground

Related

Background Execution in Xcode

I am asking if I can repeat a method in Xcode after home button pressed. Let's say every 30 minutes.
I have no idea to do it if it is possible.
I think your question is related to background execution by saying after home button pressed. First you need to address how to call the function in the background.
Here is what I recommend for you to follow:
First:
I recommend you to take a look at the apple official document.
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
Second:
There is a good tutorial:
http://pinkstone.co.uk/how-to-execute-a-method-on-a-background-thread-in-ios/
Third:
Here is what you are asking:
http://chrisrisner.com/31-Days-of-iOS--Day-23%E2%80%93Using-Background-Threads
Fourth:
If you want to explore the advanced material:
http://mobiforge.com/design-development/using-background-fetch-ios
use NSTimer
[NSTimer scheduledTimerWithTimeInterval:"your time"
target:self
selector:#selector(your method:)
userInfo:nil
repeats:YES];
There are Methods (like "viewDidLoad") that gets called for these cases. Here are some:
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (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.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
you can place the NSTimer in one of these and anything else you want to do when the home button is pressed. Hope this helps.
Source: Detect when home button is pressed iOS

NSTimer behavior on background/foreground

Scenario is:
Application has on main run loop that runs every 5 min. When app is backgrounded, the timer is not invalidated.
On foreground after waiting a bit (say 10 min), the timer immediately fires twice. I'm unclear as to what the defined behavior here should be (if any) other than on background, the timer is paused/suspended along with the app.
It seems to me that when the app is foregrounded, the timer actually looks at the elapsed time since background, determines the number of times it should have fired in between, and fires them all. I observed this scenario happening both on iOS 7 simulator and iOS 7 iphone.
Google search didn't turn up much info on this topic. Any ideas/pointers?
Update: Clarification - my question is what is the behavior for NSTimer when the application is backgrounded and then foregrounded again, assuming the timer is not invalidated/nil-ed on background, and a new timer is not created on foreground.
Code sample (code is bit old - pre ARC days):
#implementation ReportHandler {
NSTimer *_reportTimer;
}
- (id)init
{
if (_reportTimer == nil) {
_reportTimer = [[NSTimer timerWithTimeInterval:5*60 target:self selector:#selector(didFireReportTimer:) userInfo:nil repeats:YES] retain];
[[NSRunLoop mainRunLoop] addTimer:_reportTimer forMode:NSDefaultRunLoopMode];
}
}
- (void)didFireReportTimer:(NSTimer *)timer {
// send report over network here, timer is not invalidated here
}
There are no background/foreground handlers either here or in the app delegate dealing with this timer.
It seems to me that when the app is foregrounded, the timer actually looks at the elapsed time since background, determines the number of times it should have fired in between, and fires them all. I observed this scenario happening both on iOS 7 simulator and iOS 7 iphone.
That is a correct description of the behavior of NSTimer and the run loop. When your app is suspended it won't fire (by default, when you background it; but if you start a background task, it will fire as normal while the task is running).

NSTimer not stopping after the app goes into background

Until yesterday I was sure, the NSTimer will get stopped after the app goes into background. I'm have a feeling like experiencing some anomally.
My app has update location and play audio background modes. Update location is refreshed every few seconds. But it only happends on one of the app screens. There also is NSTimer refreshing some UI.
I've scheduled it like this:
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime) userInfo:nil repeats:YES];
There is also a method, which content is irrelevant now:
-(void)updateTime
{
//irrelevant content, but the method gets fired even when the app is in background
}
The weird thing is, thah the method, which is only fired by the NSTimer and nowehere else is fired even after the ap go into the background. What is happening here? Is that normal behaviour?
Because you are using background modes with location and audio your app is still alive in background.and so your timers are running.
If you remove background modes with location and audio that you are using and then try the timers wont work.
Its Normal behaviour. Correct me if i'm wrong.
Create your timer as public i mean add it in .h file and access it when your app. enter in backGround Mode
- (void)applicationDidEnterBackground:(UIApplication *)application
By above method and set your timer as inValidate.. its fix.
And if you want to do again start your timer then you can access it by
- (void)applicationWillEnterForeground:(UIApplication *)application
this method. Here you need to recreate your timer.
You can stop the timer using invalidate the timer.
[self.timer invalidate];
self.timer= nil;

Pause main execution before iOS app terminates

How can I make my app pause for a couple of seconds to invoke a message inside applicationDidEnterBackground when I hit the home button? For example:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.delegate aMessage];
// Pause for a couple of seconds to give aMessage time to finish
// Continue execution after 2 seconds
}
You can't, but you don't need to either. Your app gets to finish aMessage before it really goes into the background at which point you can't execute any code anymore.

applicationWillEnterForeground vs. applicationDidBecomeActive, applicationWillResignActive vs. applicationDidEnterBackground

Which is the proper delegate to implement when an application is waking up from being in the background and you want it to prep it to be active?
applicationWillEnterForeground vs applicationDidBecomeActive -- What's the difference?
Which is the proper delegate to implement for when an application is going to sleep and you want to prep it to cleanup and save data?
applicationWillResignActive vs. applicationDidEnterBackground -- What's the difference?
Also, I've noticed that applicationWillResignActive gets called when an incoming SMS or call comes in but the user chooses to click Ok and continue. I don't want my app to take any action in these cases. I just want it to keep running without any intermediate cleanup since the user didn't exit the app. So, I would think it makes more sense to do cleanup work just in applicationDidEnterBackground.
I would appreciate your input on best practices to follow on choosing which delegates to implement for waking up and going to sleep as well as considering events like being interrupted by SMS/calls.
Thanks
When waking up i.e. relaunching an app (either through springboard, app switching or URL) applicationWillEnterForeground: is called. It is only executed once when the app becomes ready for use, after being put into the background, while applicationDidBecomeActive: may be called multiple times after launch. This makes applicationWillEnterForeground: ideal for setup that needs to occur just once after relaunch.
applicationWillEnterForeground: is called:
when app is relaunched
before applicationDidBecomeActive:
applicationDidBecomeActive: is called:
when app is first launched after application:didFinishLaunchingWithOptions:
after applicationWillEnterForeground: if there's no URL to handle.
after application:handleOpenURL: is called.
after applicationWillResignActive: if user ignores interruption like a phone call or SMS.
applicationWillResignActive: is called:
when there is an interruption like a phone call.
if user takes call applicationDidEnterBackground: is called.
if user ignores call applicationDidBecomeActive: is called.
when the home button is pressed or user switches apps.
docs say you should
pause ongoing tasks
disable timers
pause a game
reduce OpenGL frame rates
applicationDidEnterBackground: is called:
after applicationWillResignActive:
docs say you should:
release shared resources
save user data
invalidate timers
save app state so you can restore it if app is terminated.
disable UI updates
you have 5 seconds to do what you need to and return the method
if you don't return within ~5 seconds the app is terminated.
you can ask for more time with beginBackgroundTaskWithExpirationHandler:
The official documentation.
Managing Your App's Life Cycle is helpful to your questions. For quick concept, you can see Figures in that document.
You can also read the comment from the code generated by the XCode Wizard. Listed as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state.
This can occur for certain types of temporary interruptions (such as an
incoming phone call or SMS message) or when the user quits the application
and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down
OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (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.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the active state;
here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the
application was inactive. If the application was previously in the
background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
Save data if appropriate.
See also applicationDidEnterBackground:.
*/
}
For more detailed explanations, please refer to official document for UIApplicationDelegate
I was still a bit confused with Dano's answer so I did a little test to get the flow of events in certain scenarios for my reference, but it might be useful to you too. This is for apps that DO NOT use UIApplicationExitsOnSuspend in their info.plist. This was conducted on an iOS 8 simulator + confirmed with iOS 7 device. Please excuse Xamarin's event handler names. They are very similar.
Initial and all subsequent launches from a not-running state:
FinishedLaunching
OnActivated
Interruption (phone call, top slide-down, bottom slide-up):
Home button double-press listing inactive apps, then reselecting our app:
OnResignActivation
OnActivated
Home button double-press listing inactive apps, selecting another app, then relaunching our app:
Home button single press, then relaunch:
Lock (on/off button), then unlock:
OnResignActivation
DidEnterBackground
WillEnterForeground
OnActivated
Home button double-press, and terminate our app: (subsequent relaunch is first case)
OnResignActivation
DidEnterBackground
DidEnterBackground (iOS 7 only?)
Yes, DidEnterBackground is called twice on iOS7 device. Both times UIApplication state is Background. However, iOS 8 simulator does not. This needs testing on iOS 8 device. I will update my answer when I get my hand on it, or someone else could confirm.
applicationWillEnterForeground is called:
when app is relaunched(comes from background to foreground)
This method is not invoked when app starts for the first time i.e when applicationDidFinishLaunch is called but only when comes from background
applicationDidBecomeActive
applicationDidBecomeActive is called
when app is first launched after didFinishLaunching
after applicationWillEnterForeground if there’s no URL to handle.
after application:handleOpenURL: is called.
after applicationWillResignActive if user ignores interruption like a phone call or SMS.
after disappearing of alertView anywhere from the application
applicationWillResignActive is called when system is asking for permissions. (in iOS 10). Just in case someone hit into the same trouble as me...
In iOS 8+ there is a subtle but important difference for taking phone call.
In iOS 7 if user takes phone call both applicationWillResignActive: and applicationDidEnterBackground: are called. But in iOS 8+ only applicationWillResignActive: is called.
For iOS 13+ the following methods will be executed:
- (void)sceneWillEnterForeground:(UIScene *)scene
- (void)sceneDidBecomeActive:(UIScene *)scene

Resources