@@ -677,6 +677,73 @@ - (NSString *)description
677677 return localModules;
678678}
679679
680+ @interface RCTDisplayLink : NSObject <RCTInvalidating>
681+
682+ - (instancetype )initWithBridge : (RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
683+
684+ @end
685+
686+ @interface RCTBridge (RCTDisplayLink)
687+
688+ - (void )_update : (CADisplayLink *)displayLink ;
689+
690+ @end
691+
692+ @implementation RCTDisplayLink
693+ {
694+ __weak RCTBridge *_bridge;
695+ CADisplayLink *_displayLink;
696+ }
697+
698+ - (instancetype )initWithBridge : (RCTBridge *)bridge
699+ {
700+ if ((self = [super init ])) {
701+ _bridge = bridge;
702+ _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector (_update: )];
703+ [_displayLink addToRunLoop: [NSRunLoop mainRunLoop ] forMode: NSRunLoopCommonModes ];
704+ }
705+ return self;
706+ }
707+
708+ - (BOOL )isValid
709+ {
710+ return _displayLink != nil ;
711+ }
712+
713+ - (void )invalidate
714+ {
715+ if (self.isValid ) {
716+ [_displayLink invalidate ];
717+ _displayLink = nil ;
718+ }
719+ }
720+
721+ - (void )_update : (CADisplayLink *)displayLink
722+ {
723+ [_bridge _update: displayLink];
724+ }
725+
726+ @end
727+
728+ @interface RCTFrameUpdate (Private)
729+
730+ - (instancetype )initWithDisplayLink : (CADisplayLink *)displayLink ;
731+
732+ @end
733+
734+ @implementation RCTFrameUpdate
735+
736+ - (instancetype )initWithDisplayLink : (CADisplayLink *)displayLink
737+ {
738+ if ((self = [super init ])) {
739+ _timestamp = displayLink.timestamp ;
740+ _deltaTime = displayLink.duration ;
741+ }
742+ return self;
743+ }
744+
745+ @end
746+
680747@implementation RCTBridge
681748{
682749 RCTSparseArray *_modulesByID;
@@ -685,6 +752,8 @@ @implementation RCTBridge
685752 Class _executorClass;
686753 NSURL *_bundleURL;
687754 RCTBridgeModuleProviderBlock _moduleProvider;
755+ RCTDisplayLink *_displayLink;
756+ NSMutableSet *_frameUpdateObservers;
688757 BOOL _loading;
689758}
690759
@@ -711,6 +780,8 @@ - (void)setUp
711780 _latestJSExecutor = _javaScriptExecutor;
712781 _eventDispatcher = [[RCTEventDispatcher alloc ] initWithBridge: self ];
713782 _shadowQueue = dispatch_queue_create (" com.facebook.React.ShadowQueue" , DISPATCH_QUEUE_SERIAL);
783+ _displayLink = [[RCTDisplayLink alloc ] initWithBridge: self ];
784+ _frameUpdateObservers = [[NSMutableSet alloc ] init ];
714785
715786 // Register passed-in module instances
716787 NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc ] init ];
@@ -891,6 +962,9 @@ - (void)invalidate
891962 [_javaScriptExecutor invalidate ];
892963 _javaScriptExecutor = nil ;
893964
965+ [_displayLink invalidate ];
966+ _frameUpdateObservers = nil ;
967+
894968 // Invalidate modules
895969 for (id target in _modulesByID.allObjects ) {
896970 if ([target respondsToSelector: @selector (invalidate )]) {
@@ -1075,6 +1149,26 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i
10751149 return YES ;
10761150}
10771151
1152+ - (void )_update : (CADisplayLink *)displayLink
1153+ {
1154+ RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc ] initWithDisplayLink: displayLink];
1155+ for (id <RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
1156+ if (![observer respondsToSelector: @selector (isPaused )] || ![observer isPaused ]) {
1157+ [observer didUpdateFrame: frameUpdate];
1158+ }
1159+ }
1160+ }
1161+
1162+ - (void )addFrameUpdateObserver : (id <RCTFrameUpdateObserver>)observer
1163+ {
1164+ [_frameUpdateObservers addObject: observer];
1165+ }
1166+
1167+ - (void )removeFrameUpdateObserver : (id <RCTFrameUpdateObserver>)observer
1168+ {
1169+ [_frameUpdateObservers removeObject: observer];
1170+ }
1171+
10781172- (void )reload
10791173{
10801174 if (!_loading) {
0 commit comments