I have created Application which runs NSTimer in Background. I used the Location manager to run the NSTimer in background,
I used below link to run NSTimer in background,
How do I get a background location update every n minutes in my iOS application?
This approach works fine in iOS 6 but not works on iOS 7. My Application crashes after some time while Application in background on iOS 7.
Please let me know if any different approach to run the NSTimer in background.
Thanks in advance.
In iOS7, there is a new mode for periodic data fetch. Add the fetch background mode to your app, and in your application delegate, pass an interval to - [UIApplication setMinimumBackgroundFetchInterval:. Your app's delegate will start receiving calls to application:performFetchWithCompletionHandler: once the app is in the background.
More information here:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:performFetchWithCompletionHandler:
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
loop = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(Update) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:loop forMode:NSRunLoopCommonModes];
NSTimer *currentCycleTimer;
UIBackgroundTaskIdentifier bgTask = UIBackgroundTaskInvalid;
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
[currentCycleTimer invalidate];
currentCycleTimer=nil;
secondsLeft = 120;
currentCycleTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(Countdown) userInfo:nil repeats: YES];
-(void) Countdown
{
[currentCycleTimer invalidate];
currentCycleTimer=nil;
}
-(void)start {
[[NSUserDefaults standardUserDefaults ]setObject:[NSDate date] forKey:#"startTimer"];
Nstimer* timer2=[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerFired) userInfo:nil repeats:YES];
}
-(void)timerFired
{
#try {
NSDate *timerStartDate = [[NSUserDefaults standardUserDefaults]objectForKey:#"startTimer"];
NSTimeInterval interval=[timerStartDate timeIntervalSinceNow];
int hour1=-interval/3600;
int rem =((int)interval)%3600 ;
int min1 = -rem/60 ;
int sec1 = -rem %60 ;
// NSLog(#"hour %i rem %i",hour,rem);
// NSLog(#"hour%i",hour1);
// NSLog(#"min%i",min1);
// NSLog(#"sec%i",sec1);
NSString *strmin=[NSString stringWithFormat:#"%i",min1];
NSString *strhour=[NSString stringWithFormat:#"%i",hour1];
if ([strmin integerValue]<10)
{
[lblSeconds setText:[NSString stringWithFormat:#"0%#",strmin]];
}
else
{
lblSeconds.text=strmin;
}
if ([strhour integerValue]<10) {
[lblHour setText:[NSString stringWithFormat:#"0%#",strhour]];
}
else
{
lblHour.text=strhour;
}
}
#catch (NSException *exception) {
NSLog(#"exception in timer %# ",[exception description]);
}
return;
}
Related
i am working in iPhone application, Using NSTimer to create time count to set in the screen like initially 100 then elapsed like 99,98,97 etc... if i have completed the game with available elapsed time, then i showed AlertView like successfully finished, the user press ok button navigate to previous screen, then again go to game screen, at the time ellapsed time start with previous elapsed time like 66,65,64 etc... i want, when the user go to game screen again the time count start with 100, how to fix this issue?, please help me
Thanks in Advance
I tried this:
- (void)viewDidLoad
{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(elapsedTime) userInfo:nil repeats:YES];
}
-(void)elapsedTime
{
static int i = 100;
NSLog(#"i:%d",i);
lbl.text = [NSString stringWithFormat:#"%d",i];
i--;
if(i<0)
{
[timer invalidate];
timer = nil;
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[timer invalidate];
timer = nil;
[self.navigationController popViewControllerAnimated:YES];
}
Define int i as class variable in .h file
- (void)viewDidLoad
{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(elapsedTime) userInfo:nil repeats:YES];
i = 100;
}
-(void)elapsedTime
{
i--;
if(i<0)
{
//show alertView here
[timer invalidate];
timer = nil;
}
}
The following method causes a crash. The UI is like a button, which handles the start / stop functionality of the NSTimer. If the timer runs, a UILabel is updated. Using the viewDidLoad Method makes my timer work, stopping it works too, but starting it again crashes the app.
Removing the alloc in the viewDidLoad method and trying to use the start button causes a crash instantly. Even the NSLog(#"Start now");is not called.
Code:
- (void)tick {
NSLog(#"tick");
float value = [moneyLabel.text floatValue];
moneyLabel.text = [NSString stringWithFormat:#"%f", value + 1.0];
}
- (IBAction)startStopButtonClicked:(UIButton *)sender {
if ([sender.titleLabel.text isEqualToString:#"Start"]) {
NSLog(#"Start now");
if (timer) {
NSLog(#"Timer valid");
[timer fire];
} else {
NSLog(#"Timer is nil");
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(tick) userInfo:nil repeats:YES];
[timer fire];
}
NSLog(#"bla");
[sender setTitle:#"Stop" forState:UIControlStateNormal];
} else {
[timer invalidate];
timer = nil;
NSLog(#"Stopped.");
NSLog(#"Timer isValid: %#", timer);
[sender setTitle:#"Start" forState:UIControlStateNormal];
}
}
I don't see the need to call [NSTimer fire] at all; it should be enough to allow the timer to decide when to fire.
Firstly ensure that timer is nil (it should be if it's an instance variable of the object), although explicitly setting it to nil in - (id)init won't hurt.
Next I would use the state of the timer itself to determine whether start/stop has been pressed, not the text in the button:
- (IBAction)startStopButtonClicked:(UIButton *)sender
{
if (timer != nil)
{
NSLog(#"Stopping timer");
[timer invalidate];
timer = nil;
}
else
{
NSLog(#"Starting timer");
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
}
[sender setTitle:(timer != nil ? #"Stop" : #"Start")
forState:UIControlStateNormal];
}
The code you have posted works as desired - just tested it in a new project, so the problem could be somewhere else. I tested it only by declaring the ivar NSTimer *timer; without any initialization in viewDidLoad: or the designated initializers...
In my iPhone Timer Application,
In which a timer should run in background.
So,
I have set the notification in appdelegate it works perfectly...
With that I am calling the methods from view controller which makes timer alive.
Take a look some code...
App delegate
- (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.
*/
NSLog(#"Time Remaining %d",(self.viewController.totalSeconds-self.viewController.totalCount));
[self.viewController selectandnotify:(self.viewController.totalSeconds-self.viewController.totalCount)];
[self.viewController stopTimer];
[self.viewController startTimerAction];
}
Here I am calling the method startTimerAction method which is in my view controller...take a look at this...
-(void)startTimerAction
{
timer_main = [NSTimer scheduledTimerWithTimeInterval:(1.0) target:self selector:#selector(ShowActicity) userInfo:nil repeats:YES];
}
Which is NSTimer
Here every time
-ShowActivity method will call after each second...Which is below in my view controller...
-(void)ShowActicity
{
NSLog(#"Total Counts %d",totalCount);
if (totalCount == totalSeconds) {
if ([timer_main isValid]) {
[timer_main invalidate];
isTimeOver = YES;
[self generateLog];
}
} else {
totalCount++;
seconds =seconds + 1;
if(seconds > 59)
{
minutes = minutes + 1;
seconds= 0;
}
}
How to call each time This method from view controller.....
How can I call each time showActivity method from appdelegate...
Should I use delegate for that
Should I create showActivity and timer in my Appdelegate..
Actually I want this application to run when view switches in app.....
I think If I make delegate is a good option?
Any other way....please have some suggestions
Generally use this code for background running .In the Background timer doesn't work
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
[self startTimerAction];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW3
I am using Background Thread to update one of my label
I am using the following code. But in iOS 4.0 i have learn that application saves its states and goes to background. and my application also did that work but the thread i am using stops working when i hide the application and again resumes from where i left when i reopen it. Can anybody please tell me what do I need to change in code in order to make the thread keep on running in background and change my GUI while my application is hidden. I am using this code..
-(void)startThread
{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(setUpTimerThread) object:nil];
[thread start];
}
-(void)setUpTimerThread
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSTimer *timer = [NSTimer timerWithTimeInterval:3 target:self selector:#selector(triggerTimer) userInfo:nil repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
[runLoop run];
[pool release];
}
-(void)triggerTimer
{
NSLog(#"***Timer Called after 3 seconds*** %d",count);
self.label.text = [NSString stringWithFormat:#"count value = %d",count];
//self.titleLabel.text= [NSString stringWithFormat:#"count value = %d",count];
count = count +1;
}
Thanks
Timers won't work on relaunch of the application. What you need to do is reinitialize the timer from your appDelegate's applicationDidBecomeActive: method and make sure you shut down the timer from applicationWillEnterBackground: method
I'll go straight to the problem.
This ones eating my head since a week.
What i intend to do is set my timer, which is supposed to be fired on main run loop, from a secondary thread. So i do it as follows.
if(timerRefresh)
{
//[timerRefresh invalidate];
timerRefresh = nil;
}
if (!self.isConnectionAvailable) {
timerRefresh = [NSTimer timerWithTimeInterval:appDelegate.TimerInterval target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
}
else if (self.isLivePresent||self.isUpcomingMatchToday) {
timerRefresh = [NSTimer timerWithTimeInterval:appDelegate.TimerInterval target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
}
else {
timerRefresh = [NSTimer timerWithTimeInterval:LongRefresh target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
}
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
[runLoop addTimer:timerRefresh forMode:NSDefaultRunLoopMode];
[runLoop run];
When this fires, a loader begins loading on the main thread, and the processing work is done on the secondary thread.
I hope this is a correct way.
Now i have a child class within this main class which also has to show a loader while it triggers a filtering process, so to avoid multiple loaders, when the the filtering process triggers, i pause the refreshing on this parent class, by sending it notifications from the child class..like this...
-(void)teamNameClicked:(id)sender
{
BOOL result = YES;
NSNumber *newNumber = [NSNumber numberWithBool:result];
[[NSNotificationCenter defaultCenter] postNotificationName:#"PauseMatchesLiveMatchTimer" object:newNumber];
[self performSelector:#selector(sendTeamNameClickToFunction:) withObject:sender];
}
and when operation completes i have another notifier as this...
-(void)processTeamNameClick:(id)sender
{
UIButton *button = (UIButton *)sender;
selectedIndexDropDown = button.tag;
[self parseTeamFile:button.tag];
self.lblDropDown.text = [dictTeamFilter valueForKey:[NSString stringWithFormat:#"%i",button.tag]];
[tblResults performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
BOOL result = NO;
NSNumber *newNumber = [NSNumber numberWithBool:result];
[[NSNotificationCenter defaultCenter] postNotificationName:#"PauseMatchesLiveMatchTimer" object:newNumber];
}
Notice the YES and NO for results..
Now this is an observer for the notification...
-(void)pauseAndResumeTimer:(NSNotification *)notification;
{
NSNumber *newNumber = [notification object];
BOOL result = [newNumber boolValue];
if (result) {
if(timerRefresh)
{
if ([timerRefresh isValid])
[timerRefresh invalidate];
timerRefresh = nil;
}
}
else
{
if(timerRefresh)
{
if ([timerRefresh isValid])
[timerRefresh invalidate];
timerRefresh = nil;
}
if (!self.isConnectionAvailable) {
timerRefresh = [NSTimer timerWithTimeInterval:appDelegate.TimerInterval target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
}
else if (self.isLivePresent||self.isUpcomingMatchToday) {
timerRefresh = [NSTimer timerWithTimeInterval:appDelegate.TimerInterval target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
}
else {
timerRefresh = [NSTimer timerWithTimeInterval:LongRefresh target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
}
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
[runLoop addTimer:timerRefresh forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
When the filtering process is on, i stop the parent timer. And when off i start it again.
Ok...So now the problem... When i do normal navigation on my pages, it works absolutely fine..like switching tabs, traversing between pages etc.
But if i use the filter process, somehow, it triggers my timer on the main page, and even when the view has disappeared, seems to kick off my timer event. I want to avoid that, but i just dont know how..
If anyone can genuinely help me, please do.
Thanks in advance.
There is some funny stuff going on in your code:
First and formemost, I am at least least 90% positively sure that you don't want to call [[NSRunLoop mainRunLoop] run] in any of your program's methods — do those methods even exit, or do you keep aggregating thread upon thread?
Secondly, your timer invalidations are all a bit strange:
There is no use in if (timerRefresh) if ([timerRefresh isValid]) [timerRefresh invalidate];; since in Objective C, messaging nil is perfectly fine. The result of such a message is always 0x0, so the first if is unnecessary and the second one evaluates to NO in that case, anyway.
Invalidating a timer means removing it from the runloop it was scheduled on. Hence, the second if is unnecessary, too — leaving you with just [timerRefresh invalidate];.
For -[NSTimer invalidate] to have an effect, it needs to be called on the thread the timer is scheduled on. From what I understood, this is not the case in all your methods. So you should use performSelectorOnMainThread:withObject:waitUntilDone: with the appropriate arguments instead.
There is no difference between [self performSelector:#selector(sendTeamNameClickToFunction:) withObject:sender] and simply [self sendTeamNameClickToFunction:sender]. Except that the latter is much easier to read ;-)
The if clauses in pauseAndResumeTimer: don't make an awful lot of sense, i.e. there's a lot of code duplication.
Here is said method in a tidied-up fashion and with the invalidation happening on the main thread:
-(void)pauseAndResumeTimer:(NSNotification *)notification
{
[timerRefresh performSelectorOnMainThread:#selector(invalidate) withObject:nil waitUntilDone:YES];
timerRefresh = nil;
NSNumber *result = [notification object];
if ([result boolValue]) return;
if ( !self.isConnectionAvailable || self.isLivePresent || self.isUpcomingMatchToday ) {
timerRefresh = [NSTimer timerWithTimeInterval:appDelegate.TimerInterval target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
} else {
timerRefresh = [NSTimer timerWithTimeInterval:LongRefresh target:self selector:#selector(startAutoRefresh) userInfo:nil repeats:NO];
}
[[NSRunLoop mainRunLoop] addTimer:timerRefresh forMode:NSDefaultRunLoopMode];
}