Search code examples
ioscocoa-touchbackgroundios7

iOS Timer run in background


My application requires calling a method periodically.

This call should be done even if the application has been put in the background.

The time that the application can be in the background is undefined, any time the application can go from being in the background to resume.

I have a class to manage timer:

@protocol TimerDelegate <NSObject>
- (void)startTimerAction:(id)userInfo;
@end

@interface TimerController : NSObject{
    NSTimer *repeatingTimer;
    id <TimerDelegate> delegate;
}
+ (TimerController *)sharedInstance;

- (void)startTimerForSender:(id <TimerDelegate>)sender withTimeInterval:(NSTimeInterval)time;
- (void)stopTimer;

@end

Implementation:

#import "TimerController.h"

TimerController *singletonInstance;

@implementation TimerController

#pragma mark - Singleton
+ (TimerController *)sharedInstance {

    if (singletonInstance == nil) {
        singletonInstance =  [[super allocWithZone:NULL] init];
    }
    return singletonInstance;
}

+ (id)allocWithZone:(NSZone *)zone
{
    return [self sharedInstance];
}

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

#pragma mark - Public
- (void)startTimerForSender:(id <TimerDelegate>)sender withTimeInterval:(NSTimeInterval)time
{
    if (repeatingTimer) {
        [self stopTimer];
    }

    if (sender) {
        delegate = sender;
    }

    if (time > 0.0) {
        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(targetMethod:)
                                                        userInfo:[self userInfo] repeats:YES];
        repeatingTimer = timer;
    }
}

- (void)stopTimer
{
    if (repeatingTimer) {
        [repeatingTimer invalidate];
        repeatingTimer = nil;
    }
}

- (NSDictionary *)userInfo
{
    return @{@"StartDate" : [NSDate date]};
}

- (void)targetMethod:(NSTimer*)theTimer
{
    if (delegate && [delegate respondsToSelector:@selector(startTimerAction:)]) {
        [delegate startTimerAction:[repeatingTimer userInfo]];
    }
}


@end

I use the TimerController class in this way:

-(void)viewDidLoad{
[super viewDidLoad]
TimerController *timerC = [TimerController sharedInstance];
[timerC startTimerForSender:self withTimeInterval:30];

}

#pragma mark - TimerDelegate

- (void)startTimerAction:(id)userInfo
{
NSLog(@"Method called");
}

I can read in NSLog entries every 30 seconds. Put the application in the background makes the timer stops and it do not write logs every 30 seconds, when the application resume the timer works correctly.

I need to call method every 30 seconds although the application is in the background.

I read this question: Timer in background mode

I must be able to submit application to AppStore.

Thanks


Solution

  • - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            // Clean up any unfinished task business by marking where you
            // stopped or ending the task outright.
            [application 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, preferably in chunks.
     
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        });
    }
    

    source: iPhone Programmig Guide

    similar question: https://stackoverflow.com/a/8613624/1827690