NSTimer does not stop when invalidate - ios

I want to check if the application is idle (user doesn't take any action for the last 30 minutes) and log out the user.
For that I have an event manager that resets a timer.
- (void)sendEvent:(UIEvent *)event {
if(event == nil) {
[self resetIdleTimer];
} else {
[super sendEvent:event];
// Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets.
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0) {
// allTouches count only ever seems to be 1, so anyObject works here.
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded)
[self resetIdleTimer];
}
}
}
- (void)resetIdleTimer {
if (self.idleTimer) {
[self.idleTimer invalidate];
self.idleTimer = nil;
}
NSInteger maxTime = 60*30; //30 minutes
self.idleTimer = [NSTimer scheduledTimerWithTimeInterval:maxTime target:self selector:#selector(checkIfIdleTimerExceeded) userInfo:nil repeats:NO];
}
- (void)checkIfIdleTimerExceeded {
theProfileManager = [[ProfileManager alloc] init];
theProfile = [theProfileManager getProfile];
if( ! [[theProfile getStatus]isEqualToString:#"u1"]) {
if( ! [[theProfile getTheUser] isUnlimitedLogin]) {
theProfile = nil;
theUserManager = [[UserManager alloc] init];
[theUserManager setCurrentUser:[theProfile getTheUser]];
[theUserManager setCurrentUserAccount:[[theProfile getTheUser] getTheUserAccount]];
[theUserManager setCurrentUserSettings:[[theProfile getTheUser] getTheUserSettings]];
[theUserManager logoutCurrentUser];
[[MenuItemDataManager alloc] deleteJsonData];
NSNotification *msg = [NSNotification notificationWithName:#"leftPanelMsg" object:[[NSString alloc] initWithFormat:#"Home"]];
[[NSNotificationCenter defaultCenter] postNotification:msg];
[self performSelector:#selector(loadHomeView:) withObject:nil afterDelay:0.5f];
}
}
[self resetIdleTimer];
}
The checkIfIdleTimerExceeded does the log out process.
Problem: After 15 minutes I touch the screen, the resetIdleTime is called and should restart a new NSTimer. But after 15 minutes the application logs the user out.
Thanks for your help.
André.

As per your requirement of the Touches event. You should do this:
NSSet *allTouchEvents = [event allTouches];
if ([allTouchEvents count] > 0) {
// allTouchEvents count only ever seems to be 1, so anyObject works here.
UITouchPhase phase = ((UITouch *)[allTouchEvents anyObject]).phase;
if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded)
[self resetIdleTimer];
}
And in the resetIdleTimer, The
self.idleTimer = [NSTimer scheduledTimerWithTimeInterval:maxTime target:self selector:#selector(checkIfIdleTimerExceeded) userInfo:nil repeats:NO];
should be like
self.idleTimer = [NSTimer scheduledTimerWithTimeInterval:maxTime target:self selector:#selector(checkIfIdleTimerExceeded:) userInfo:nil repeats:NO];
And checkIfIdleTimerExceeded should be like:
-(void) checkIfIdleTimerExceeded :(NSTimer*)timer
{
//Do your all process and invalidate after completion
[timer invalidate];
}

I'm not sure which one of your other methods can be called beside the –resetIdleTimer in your workflow when you touch the screen, but that solution works to me as you have described to like getting it worked:
I'm seeing in the log console the –resetTimer: will called if I tap a button within 7 secs, it resets the timer properly and creates a new 7 secs timer.
the logout method will call only if I don't touch my button on the screen in 7 secs time, and the timer finally invokes the –logout: method.
- (void)resetTimer:(NSTimer *)timer {
NSLog(#"reset time...");
if (timer.isValid) [timer invalidate], _timer = nil;
_timer = [NSTimer scheduledTimerWithTimeInterval:7.f target:self selector:#selector(logout:) userInfo:nil repeats:NO];
}
//
- (void)buttonTouchedUpInside:(UIButton *)sender {
NSLog(#"touched...");
[self resetTimer:_timer];
}
//
- (void)logout:(NSTimer *)timer {
NSLog(#"logout...");
if (timer.isValid) [timer invalidate], _timer = nil;
// logout ...
}

Related

iOS: Why can I not call performSelector to execute the same function where it is called?

When I use [self performSelector:#selector(checkFirstSynchro) withObject:nil afterDelay:30.0]; in the checkFirstSynchro function, it seems not to work. Is it because it is in a Class function?
-(void) viewDidLoad
{
...
[self checkFirstSynchro]
...
}
- (void) checkFirstSynchro
{
//Check if firstSynchro
BOOL firstSynchro = [[[NSUserDefaults standardUserDefaults] valueForKey:#"FirstSynchro"] boolValue];
if (!firstSynchro)
{
[Reachability checkInternetConnectivityWithSuccessCompletion:^(NSError *error) //test if there is internet c
onnection
{
if (error == nil)
{
[self sync:connectBtn];
}
else
{
[self performSelector:#selector(checkFirstSynchro) withObject:nil afterDelay:30.0];
}
}];
}
}
It launches well the function, but it doesn't execute the perform 30.0 seconds later.
I tried this instead of the performSelector, but without success:
[NSTimer scheduledTimerWithTimeInterval:TIMER_FIRST_SYNCHRO
target:self
selector:#selector(checkFirstSynchro)
userInfo:nil
repeats:NO];
It launches well the function, but it doesn't execute the [NSTimer scheduledTimerWithTimeInterval 30.0 seconds later.
Thanks in advance.

I want to enable Timer only when user is logged in

I want to enable Timer only when user is logged in. How to go about this? I tried but getting session time out message even when user is on login screen.
#import "TIMERUIApplication.h"
#implementation TIMERUIApplication
//here we are listening for any touch. If the screen receives touch, the timer is reset
-(void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
if (!myidleTimer)
{
[self resetIdleTimer];
}
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0)
{
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan)
{
[self resetIdleTimer];
}
}
}
-(void)resetIdleTimer
{
if (myidleTimer)
{
[myidleTimer invalidate];
}
//convert the wait period into minutes rather than seconds
int timeout = kApplicationTimeoutInMinutes * 60;
myidleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self
selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
}
//if the timer reaches the limit as defined in kApplicationTimeoutInMinutes, post this notification
-(void)idleTimerExceeded {
[[NSNotificationCenter defaultCenter]
postNotificationName:kApplicationDidTimeoutNotification object:nil];
}
and
-(void)applicationDidTimeout:(NSNotification *) notif {
NSLog (#"time exceeded!!");
UIViewController *controller = [[UIStoryboard
storyboardWithName:#"MainStoryboard" bundle:NULL]
instantiateViewControllerWithIdentifier:#"mainView"];
//I've tried a few varieties of the if statement to no avail. Always goes to else.
if ([controller isViewLoaded]) {
NSLog(#"Already there!");
}
else {
NSLog(#"go home");
[(UINavigationController *)self.window.rootViewController pushViewController:controller animated:YES];
//[(TIMERUIApplication *)[UIApplication sharedApplication] resetIdleTimer];
}
}

how to resume time counter value in when i start the app

I want to resume time counter value in when I start the app.like first my label value is 05:00 than after close the app but when I start the app agin that time timer count start to 05:00. Please help me
int timeSec,timeMin ;
- (void)viewDidLoad {
timeSec=0;
timeMin=0;
[self StartTimer];
}
-(void) StartTimer
{
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
//Event called every time the NSTimer ticks.
- (void)timerTick:(NSTimer *)timer
{
timeSec++;
if (timeSec == 60)
{
timeSec = 0;
timeMin++;
}
//Format the string 00:00
NSString* timeNow = [NSString stringWithFormat:#"%02d:%02d", timeMin, timeSec];
//Display on your label
//[timeLabel setStringValue:timeNow];
self.lbl_timer.text= timeNow;
}
//Call this to stop the timer event(could use as a 'Pause' or 'Reset')
- (void) StopTimer
{
// [timer invalidate];
timeSec = 0;
timeMin = 0;
//Since we reset here, and timerTick won't update your label again, we need to refresh it again.
//Format the string in 00:00
NSString* timeNow = [NSString stringWithFormat:#"%02d:%02d", timeMin, timeSec];
//Display on your label
// [timeLabel setStringValue:timeNow];
self.lbl_timer.text= timeNow;
}
thanks for advance..
Try this code.store data in nsuserdefault
- (void)viewDidLoad {
timeSec=[[NSString stringWithFormat:#"%ld",(long)[[NSUserDefaults standardUserDefaults]integerForKey:#"timeSec"]] intValue];
timeMin=[[NSString stringWithFormat:#"%ld",(long)[[NSUserDefaults standardUserDefaults]integerForKey:#"timeMin"]] intValue];
[self StartTimer];
}
-(void) StartTimer
{
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
self.lbl_timer.text=#"00:00";
}
- (void)timerTick:(NSTimer *)timer
{
timeSec++;
if (timeSec == 60)
{
[locationManager startUpdatingLocation];
timeSec = 0;
timeMin++;
}
self.lbl_timer.text= [NSString stringWithFormat:#"%02d:%02d", timeMin, timeSec];;
}
when you back to the screen that time add this code.like back button or close button action
[timer invalidate];
[[NSUserDefaults standardUserDefaults]setInteger:timeSec forKey:#"timeSec"];
[[NSUserDefaults standardUserDefaults]setInteger:timeMin forKey:#"timeMin"];
Declare two variable in appdelegate file
1)NSUserDefaults *usedefaults;
2)#property (nonatomic,readwrite) int timeSec,timeMin,isBackground;
- (void)applicationDidEnterBackground:(UIApplication *)application
{
self.isBackground=1;
usedefaults=[NSUserDefaults standardUserDefaults];
[usedefaults setObject:[NSString stringWithFormat:#"%d",self.timeMin] forKey:#"timemin"];
[usedefaults setObject:[NSString stringWithFormat:#"%d",self.timeSec] forKey:#"timesec"];
NSLog(#"Enter in back value userdefault %#",usedefaults);
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
if(self.isBackground==1)
{
self.isBackground=0;
self.timeMin=[[usedefaults objectForKey:#"timemin"] intValue];
self.timeSec=[[usedefaults objectForKey:#"timesec"] intValue];
NSLog(#"Foreground value min %d sec %d",self.timeMin,self.timeSec);
}
}
In ViewDidload in your Timer startpage
app=(AppDelegate *)[[UIApplication sharedApplication] delegate];
finally just replace you self.timemin to app.timemin same for timesec.
Cheers enjoy That surely work for u ..

Managing Timers Passed as Variables

I am trying to perform what I thought was a simple task. I have a handful of repeating timers that need to be started and stopped.
I created methods for starting and stopping the timers, and attempt to pass the timers to the methods as parameters.
The problem is that the timers never seem to be stopped. Any ideas why this might not be working properly? Thank you!
Top of File:
#import "ViewController.h"
NSTimer *launchTimer;
NSTimer *transactionTimer;
Starting Method:
-(void) startingMethod {
NSString *urlString = #"http://google.com";
[[AsynchRequestService sharedInstance]
performAsynchronousURLRequest:urlString completion:^(BOOL success,
NSString *responseBody, NSString *responseStatus) {
if (success) {
[self stopResponseTimer:launchTimer];
}
else {
[self startResponseTimer:launchTimer
method:#selector(startingMethod)
interval:10];
}
}];
}
Method to Start the Timer:
-(void)startResponseTimer:(NSTimer *) timer method:(SEL) method
interval:(int) interval {
[timer invalidate];
timer = nil;
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:method userInfo:nil repeats:YES];
}
Method to Stop the Timer:
-(void)stopResponseTimer:(NSTimer *) timer {
NSLog(#"STOP TIMER");
[timer invalidate];
timer = nil;
}
Make startResponseTimer and `stopResponseTimer' take the pointer to the object pointer intead.
-(void)startResponseTimer:(NSTimer **) timer method:(SEL) method
interval:(int) interval {
[*timer invalidate];
*timer = nil;
*timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:method userInfo:nil repeats:YES];
}
-(void)stopResponseTimer:(NSTimer **) timer {
NSLog(#"STOP TIMER");
[*timer invalidate];
*timer = nil;
}
then invoke it like
[self startResponseTimer:&launchTimer];
[self stopResponseTimer:&launchTimer];
This should make sure that you retain the right NSTimer object.
NOTE:It is always a good idea to check a pointer to a pointer to an object for NULL in a public method

how to make NSTimer consistent on xcode

I'm making a game and would like to use a timer to countdown an event, just like what's seen on Bejeweled. I know that I've to put NSTimer in a NSRunLoop to make it work, since NSTimer is inaccurate. Have tried the following but it still don't work. Please help!
#import ...
NSTimer *_gameTimer;
int secondsLeft;
//some code
//called countdownTimer using [self countdownTimer];
- (void)countdownTimer
{
_gameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
NSRunLoop *gameRun = [NSRunLoop currentRunLoop];
[gameRun addTimer:_gameTimer forMode:NSDefaultRunLoopMode];
}
- (void)updateTime:(NSTimer *)timer
{
if (secondsLeft>0 && !_gameOver) {
_timerLabel.text = [NSString stringWithFormat:#"Time left: %ds", secondsLeft];
secondsLeft--;
} else if (secondsLeft==0 && !_gameOver) {
// Invalidate timer
[timer invalidate];
[self timerExpire];
}
}
- (void)timerExpire
{
// Gameover
[self gameOver];
[_gameTimer invalidate];
_gameTimer = nil;
}
NSTimer needs to be a local variable so there can only be one instance of the object running through the loop. Here's the code.
- (void)countdownTimer
{
NSTimer *_gameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
NSRunLoop *gameRun = [NSRunLoop currentRunLoop];
[gameRun addTimer:_gameTimer forMode:NSDefaultRunLoopMode];
if (secondsLeft==0 || _gameOver) {
[_gameTimer invalidate];
_gameTimer = nil;
}
}
- (void)updateTime:(NSTimer *)timer
{
if (secondsLeft>0 && !_gameOver) {
_timerLabel.text = [NSString stringWithFormat:#"Time left: %ds", secondsLeft];
secondsLeft--;
} else if (secondsLeft==0 || _gameOver) {
// Invalidate timer
[timer invalidate];
[self timerExpire];
}
- (void)timerExpire
{
// Gameover
[self gameOver];
}

Resources