diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index ef4ec94819e3eb..7568dff8a58a8f 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -97,6 +97,7 @@ @implementation RCTTiming NSTimer *_sleepTimer; BOOL _sendIdleEvents; BOOL _inBackground; + UIBackgroundTaskIdentifier _backgroundTaskIdentifier; } @synthesize bridge = _bridge; @@ -112,6 +113,7 @@ - (void)setBridge:(RCTBridge *)bridge _paused = YES; _timers = [NSMutableDictionary new]; _inBackground = NO; + _backgroundTaskIdentifier = UIBackgroundTaskInvalid; for (NSString *name in @[UIApplicationWillResignActiveNotification, UIApplicationDidEnterBackgroundNotification, @@ -135,10 +137,35 @@ - (void)setBridge:(RCTBridge *)bridge - (void)dealloc { + [self markEndOfBackgroundTaskIfNeeded]; [_sleepTimer invalidate]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (void)markStartOfBackgroundTaskIfNeeded +{ + if (_backgroundTaskIdentifier == UIBackgroundTaskInvalid) { + __weak typeof(self) weakSelf = self; + // Marks the beginning of a new long-running background task. We can run the timer in the background. + _backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + // Mark the end of background task + [strongSelf markEndOfBackgroundTaskIfNeeded]; + }]; + } +} + +- (void)markEndOfBackgroundTaskIfNeeded +{ + if (_backgroundTaskIdentifier != UIBackgroundTaskInvalid) { + [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier]; + _backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } +} + - (dispatch_queue_t)methodQueue { return RCTJSThread; @@ -163,6 +190,7 @@ - (void)appDidMoveToBackground - (void)appDidMoveToForeground { + [self markEndOfBackgroundTaskIfNeeded]; _inBackground = NO; [self startTimers]; } @@ -260,6 +288,7 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update } if (_inBackground) { if (timerCount) { + [self markStartOfBackgroundTaskIfNeeded]; [self scheduleSleepTimer:nextScheduledTarget]; } } else if (!_sendIdleEvents && timersToCall.count == 0) { @@ -338,6 +367,7 @@ - (void)timerDidFire } if (_inBackground) { + [self markStartOfBackgroundTaskIfNeeded]; [self scheduleSleepTimer:timer.target]; } else if (_paused) { if ([timer.target timeIntervalSinceNow] > kMinimumSleepInterval) {