Invalidate an NSTimer when going into background - iphone

I'm trying to invalidate a timer when my app goes into background. The timer gets invoked when you hit a button that starts the timer and is in the TimerController.m file. Here is how it gets invoked.
mytimer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(updateTime) userInfo:nil repeats:YES];//Timer with interval of one second
[[NSRunLoop mainRunLoop] addTimer:mytimer forMode:NSDefaultRunLoopMode];
Now, I'd like to invalidate mytimer when the app goes into background, so I tried putting
[mytimer invalidate];
into the - (void)applicationDidEnterBackground:(UIApplication *)application method the apps delegate. But this won't work since it's undeclared in the delegate. I thought by including TimerController.h into the delegate, this would work, but it won't.
So, I clearly don't know what I'm doing here. Can you help? How do it get it so that mytimer is invalidated when the app goes into background?

There’s also a UIApplicationDidEnterBackgroundNotification notification posted when the application goes into background. You can subscribe for this notification in your controller and handle the transition there:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(goBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
// and later:
- (void) goBackground {
[timer invalidate], timer = nil;
}

if (timer) {
[timer invalidate];
timer = nil;
}
in applicationReEnteredForeground notification method will also work

Related

applicationDidEnterBackground and applicationWillEnterForeground method are not called when pressed home button in iOS simulator

I need a long running task to be done in background as well as in foreground. This updates the core data. So to maintain UI responsive I created an another thread where I use different managedObjectContext(MOC). So a timer is set in background as well as in foreground and is inactivated appropriately when state changes. Before the task is starting and after the task is completed when I press home button it calls the two delegate methods properly but during the task is active when I press home button screen changes and UI hangs (becomes blank) but the two delegate methods are not called properly and the app is not terminated. I could not find the reason why this happens so. It would be helpful if someone can help.
I will attach the required code with this :
-(void) startTimerThread
{
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Add code here to do background processing
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]];
self.managedObjectContext = context;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:context];
NSLog(#"managedObjContext : %#\n",self.managedObjectContext);
[self getDataFromFile];
dispatch_async( dispatch_get_main_queue(), ^{
// Add code here to update the UI/send notifications based on the
// results of the background processing
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadAppDelegateTable" object:nil];
[context release];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:context];
});
});
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Background\n");
[self.notificationTimer invalidate];
self.notificationTimer = nil;
UIApplication *app = [UIApplication sharedApplication];
self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//start location update timer and background timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:180 target:self
selector:#selector(startLocationServices) userInfo:nil repeats:YES];
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
self.logDownloader.managedObjectContext = self.managedObjectContext;
NSLog(#"managedObjContext : %#\n",self.logDownloader.managedObjectContext);
self.backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:90 target:self.logDownloader selector:#selector(getDataFromFile) userInfo:nil repeats:YES];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"Foreground\n");
//invalidate background timer and location update timer
[self.timer invalidate];
[self.backgroundTimer invalidate];
self.timer = nil;
self.notificationTimer = nil;
self.logDownloader.managedObjectContext = self.managedObjectContext;
NSLog(#"managedObjContext : %#\n",self.logDownloader.managedObjectContext);
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadAppDelegateTable" object:nil];
self.notificationTimer = [NSTimer scheduledTimerWithTimeInterval:180 target:self.logDownloader selector:#selector(startTimerThread) userInfo:nil repeats:YES];
}
On iOS13+, if you implement UIWindowSceneDelegate, it calls func sceneDidEnterBackground(_ scene: UIScene), instead.
The reason why applicationDidEnterBackground: and applicationDidEnterForeground: are never called is because these methods are used in joint with Application does not run in background this option can be found in your ***-info.plist. If this option is set to YES than your app will never call these methods, because these when you press the home button with an app that has set the option to YES the instance of the app that is running will get terminated so everytime you press the home button and then select the app icon a new instance is being created so it is using applicationWillTerminate:.
The methods that Kirti mali has said would also be the incorrect methods to use for want you are after, the reason being is that applicationDidBecomeActive: and applicationWillResignActive: are used when something like when you answer a phone call. The instance running is not terminated neither is it sent to the background. The instance is paused until the user has finished on that call when it will become active again.
So the solution to this would be if you want the app to run in background would be to change the option "Application does not run in background" in the ***-info.plist to be NO just applicationDidBecomeActive: and applicationWillResignActive: is the wrong way for these methods to be used.
Please see the apple documentation on UIApplicationDelegate to get a better understanding of these methods.
Since your App runs on background those methods will never called however you can use willEnterForegroundNotification and didEnterBackgroundNotification to do same thing you want.
You can write following codes inside the didFinishLaunchingWithOptions method of ApplicationDelegate
NotificationCenter.default.addObserver(forName:UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { (_) in
// Your Code here
}
NotificationCenter.default.addObserver(forName:UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { (_) in
// Your Code here
}
This method is called when home button is pressed
- (void)applicationDidBecomeActive:(UIApplication *)application
and this method is called when icon button is pressed
- (void)applicationWillResignActive:(UIApplication *)application

when should I dismiss the timer?

I have a timer which will be initialized on viewDidLoad, and be released on viewDidUnload, but I found the viewDidUnload nerver be called and each time the view is presented to user the viewDidLoad is called, so I have many timers running simultaneously... which is not what I want.
I just want to stop&release the timer when user navigate away from this view, how can I do that?
Don't initialize it on view did load, instead initialize it in view will apper and stop it in view will dissapear, that way it will stop when you navigate away and restart once you come back to that view.
I had the exact same problem you are having. This is the code just in case you need it:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSLog(#"View will appear");
myTimer = [NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(pageCycler) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer: myTimer forMode: NSDefaultRunLoopMode];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(#"View will dissapear");
[myTimer invalidate];
myTimer=nil;
}

Releasing an NSTimer iPhone?

I have an NSTimer declared in my .h and in the viewDidLoad of the /m I have the code:
timer = [NSTimer scheduledTimerWithTimeInterval:kComplexTimer target:self selector:#selector (main) userInfo:nil repeats:YES];
I also have [timer release]; in my dealloc.
However when I exit the view and return to it, the timer has not in fact released, it has doubles in speed! How do I solve this & what am I doing wrong???
Thanks
you don't need to release it as you have not retained it - as a rule.
all you need to do is just call [timer invalidate]; which will stop your timer.
Nice Answer , but good to check whether the time is nil or not to avoid unwanted exception..
if( timer ! = nil )
{
[timer invalidate];
timer = nil;
}
Thank you...
[timer invalidate];
timer = nil;
The second line is important if you want to reset the NSTimer
You must not call release on a object that it not be created by "new", "alloc", "retain", "copy".
In this case, you had created a Timer by scheduledTimerWithTimeInterval method, So you must not call release method but call [timer invalidate] to stop the timer.

NSTimer getting zombie

I have created a NSTimer in my application which gets fired after every 1 min interval. my problem is when I put the application in background and after some time say 5 min, i bring that in foreground, the timer object gets zombied.
any thoughts on this.
Perhaps invalidate the timer when the application enters the background then start it again when the application enters the foreground.
Like so:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[myTimer invalidate];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
myTimer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:#selector(TimerFunction) userInfo:nil repeats:YES];
}
NSTimer objects are retained when scheduled. Your timer might be invalidated for some reason and by thus, released.

How to cancel a previously scheduled event in Objective-C?

I am making an iphone app and have scheduled an event called gameOver to occur after 15 seconds(when the game is over).
[self performSelector:#selector(gameOver) withObject:nil afterDelay:15.0];
How can I unschedule this event so that if someone wanted to press a reset button and start the game over, this scheduled event would be deleted and another one would be created.
I would use an NSTimer
with the retain-property NSTimer *myTimer
-(void) startTimer
{
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:myDelay target:self selector:#selector(myTimerExpiredMethod:) userInfo:nil repeats:NO];
}
-(void) myTimerExpiredMethod:(id)aSender
{
NSLog(#"timer expired");
self.myTimer = nil;
}
-(void) cancelTimer
{
[self.myTimer invalidate];
self.myTimer = nil;
}
Try
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget
aTarget would be the object the selector should be performed on. self in your example.
Edit:
This method is defined on NSObject so:
[NSObject cancelPreviousPerformRequestsWithTarget:myObject]
would be the syntax.
Edit 2:
Use
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument
to only unschedule calls to this specific selector.