Skip to content

Commit

Permalink
Fire timers in the background exclusively via NSTimer
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesreggio committed Sep 24, 2018
1 parent 93717e3 commit be12c32
Showing 1 changed file with 35 additions and 5 deletions.
40 changes: 35 additions & 5 deletions React/Modules/RCTTiming.m
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ @implementation RCTTiming
NSMutableDictionary<NSNumber *, _RCTTimer *> *_timers;
NSTimer *_sleepTimer;
BOOL _sendIdleEvents;
BOOL _inBackground;
}

@synthesize bridge = _bridge;
Expand All @@ -110,20 +111,21 @@ - (void)setBridge:(RCTBridge *)bridge

_paused = YES;
_timers = [NSMutableDictionary new];
_inBackground = NO;

for (NSString *name in @[UIApplicationWillResignActiveNotification,
UIApplicationDidEnterBackgroundNotification,
UIApplicationWillTerminateNotification]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(stopTimers)
selector:@selector(appDidMoveToBackground)
name:name
object:nil];
}

for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
UIApplicationWillEnterForegroundNotification]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(startTimers)
selector:@selector(appDidMoveToForeground)
name:name
object:nil];
}
Expand All @@ -148,8 +150,29 @@ - (void)invalidate
_bridge = nil;
}

- (void)appDidMoveToBackground
{
// Deactivate the CADisplayLink while in the background.
[self stopTimers];
_inBackground = YES;

// Issue one final timer callback, which will schedule a
// background NSTimer, if needed.
[self didUpdateFrame:nil];
}

- (void)appDidMoveToForeground
{
_inBackground = NO;
[self startTimers];
}

- (void)stopTimers
{
if (_inBackground) {
return;
}

if (!_paused) {
_paused = YES;
if (_pauseCallback) {
Expand All @@ -160,7 +183,7 @@ - (void)stopTimers

- (void)startTimers
{
if (!_bridge || ![self hasPendingTimers]) {
if (!_bridge || _inBackground || ![self hasPendingTimers]) {
return;
}

Expand Down Expand Up @@ -225,7 +248,11 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
// Switch to a paused state only if we didn't call any timer this frame, so if
// in response to this timer another timer is scheduled, we don't pause and unpause
// the displaylink frivolously.
if (!_sendIdleEvents && timersToCall.count == 0) {
if (_inBackground) {
if (_timers.count) {
[self scheduleSleepTimer:nextScheduledTarget];
}
} else if (!_sendIdleEvents && timersToCall.count == 0) {
// No need to call the pauseCallback as RCTDisplayLink will ask us about our paused
// status immediately after completing this call
if (_timers.count == 0) {
Expand Down Expand Up @@ -295,7 +322,10 @@ - (void)timerDidFire
targetTime:targetTime
repeats:repeats];
_timers[callbackID] = timer;
if (_paused) {

if (_inBackground) {
[self scheduleSleepTimer:timer.target];
} else if (_paused) {
if ([timer.target timeIntervalSinceNow] > kMinimumSleepInterval) {
[self scheduleSleepTimer:timer.target];
} else {
Expand Down

0 comments on commit be12c32

Please sign in to comment.