Timer: how to remain timer active in Background - iphone

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

Related

How to keep NSTimer alive in Background on iOS 7?

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;
}

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

beginBackgroundTaskWithExpirationHandler never gets called

I have an app that plays audio in the background. I'm trying to use beginBackgroundTaskWithExpirationHandler to skip to the next track when the current track is finished.
Here is the code that is called when the playback state changes. It never logs the "beginBG called" even though other code in the same method implements successfully in the background.
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[self ffwButtonPressed:ffwButton];
NSLog(#"beginBG called");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
ffwButtonPressed invokes a few different methods to change the track. When that is complete...
UIApplication *app = [UIApplication sharedApplication];
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
NSLog(#"end bgTask");
Edit: Declaration
UIBackgroundTaskIdentifier bgTask;
Edit: In ViewDidLoad
bgTask = 0;
You are actually misunderstanding the function
-(UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler
The block argument called "handler" is what will happen when the background task expire (10min).
To get your code running you need to put your code out of the expiration handler:
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
};
[self ffwButtonPressed:ffwButton];
NSLog(#"beginBG called");
[app endBackgroundTask:bgTask];
Here is a link to the documentation
I ended up not needing beginBackgroundTaskWithExpirationHandler at all. This line solved my problem...
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
Does your App have a permission to run in the background as an Audio App? (need to put the right mode in your Info.plist)
See Apple's docs
beginBackgroundTaskWithExpirationHandler handler only get called when timer runout which is fairly large value. Real task as mentioned by Tomsum above should not be part part of block. Also remember to call endBackgroundTask: at end if backGroundTaskID != UIBackgroundTaskInvalid and only thing need to be done in block is setting back task variable = UIBackgroundTaskInvalid
Swift Version:
var backgroundTask: UIBackgroundTaskIdentifier? = 0
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "backgroundTask", expirationHandler: {() -> Void in
backgroundTask = UIBackgroundTaskInvalid
})
// Perform actions in background
YOUR_CODE_GOES_HERE (e.g. fetch requests, etc.)
UIApplication.shared.endBackgroundTask(backgroundTask!)
there can be a little modify, we should add endBackgroundTask in expiration handler:
IApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
};
[self ffwButtonPressed:ffwButton];
NSLog(#"beginBG called");
[app endBackgroundTask:bgTask];
Is this all your code? Maybe the bgTask variable is not initialized, You need to declare bgTask before you assign. Try this -
UIBackgroundTaskIdentifier bgTask = nil;

NSTimer in Objective C when Click on Home Button

I have NSTimer in ViewController Class.. And all the methods for NStimer are in that class.. My NSTimer is working properly for play and pause... When i press home button on iPhone also the timer is working properly(it starts running from the time where the application enters into background when the application enters into foreground)... In my application i am rising an alert quickly when my application enters into foreground(UIAlertview in application didEnterForeGround). Here my NSTimer is running when the alert is on the screen(didn't give response to alert).. I want stop the NSTimer Upto the User responding to my alert... After that I want to start the NSTimer... How Can i do that...? Please help me... Thanks in Advance... I want call the NSTimer methods from Appdelegate.m file... Iam calling these methods properly... And the methods in ViewController are called.. But the action is Not Performing... For the Same method when call from viewController class it is working...
enter code here
ViewController.h
+(ExamViewController *)evcInstance;
ViewController.m
ExamViewController *evc = nil;
#implementation ExamViewController
+(ExamViewController *)evcInstance
{
if(!evc)
{
evc = [[ExamViewController alloc] init];
}
return evc;
}
- (void)onStartPressed
{
stopwatchtimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimer:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:stopwatchtimer forMode:NSRunLoopCommonModes];
resume.hidden = YES;
resume.enabled=NO;
pause.hidden = NO;
pause.enabled=YES;
}
- (void)onStopPressed
{
[stopwatchtimer invalidate];
stopwatchtimer = nil;
pause.hidden = YES;
pause.enabled=NO;
resume.hidden = NO;
resume.enabled=YES;
}
Appdelegate.m
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[ExamViewController evcInstance] onStopPressed];
NSLog(#"applicationWillEnterForeground");
if(viewCalled ==YES)
{
UIAlertView *alertview=[[UIAlertView alloc]initWithTitle:#"" message:#"DO! You Want To continue" delegate:self cancelButtonTitle:#"YES" otherButtonTitles:#"NO", nil];
[alertview show];
[alertview release];
}
/*
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
*/
}
a few things:
1) It looks like an incomplete singleton design. If you really want a singleton, refer to a post like this one.
2) No need to add stopwatchtimer to the current run loop. The scheduledTimerWithTimeInterval method schedules it for you.
3) I don't see where you declare stopwatch timer, but be sure to declare it as a retained property (or strong in ARC).
4) To stop the timer, just call [stopwatchtimer invalidate]; To restart it, re-instantiate and overwrite the old one using the same scheduledTimerWithTimeInterval class method that you called originally.
5) To do anything when an alert view completes, implement the delegate method:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
So you can invalidate the timer before presenting the alert, then rebuild it when you get notified the alert is finished.

Invalidate an NSTimer when going into background

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