From 402ae2f01fd91051be5b717b0578e18b863854af Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 20 Feb 2018 22:11:55 -0800 Subject: [PATCH] New UIManager API allowing intercept/delay mounting process Summary: In some embedding/interop cases (RN inside, something else outside), the interop layer has to have the ability to control (intercept, delay, perform synchronously with another stuff) mounting process. This API allows doing that. Reviewed By: fkgozali Differential Revision: D7014179 fbshipit-source-id: 04036095f7e60a5ff7e69025ff6066fea92eb361 --- React/Modules/RCTUIManager.m | 31 ++++++++++++++----- .../Modules/RCTUIManagerObserverCoordinator.h | 9 ++++++ .../RCTUIManagerObserverCoordinator.mm | 14 +++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 1ea715c94..5dcce4658 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1081,6 +1081,26 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag return; } + __weak typeof(self) weakSelf = self; + + void (^mountingBlock)(void) = ^{ + typeof(self) strongSelf = weakSelf; + + @try { + for (RCTViewManagerUIBlock block in previousPendingUIBlocks) { + block(strongSelf, strongSelf->_viewRegistry); + } + } + @catch (NSException *exception) { + RCTLogError(@"Exception thrown while executing UI block: %@", exception); + } + }; + + if ([self.observerCoordinator uiManager:self performMountingWithBlock:mountingBlock]) { + completion(); + return; + } + // Execute the previously queued UI blocks RCTProfileBeginFlowEvent(); RCTExecuteOnMainQueue(^{ @@ -1088,14 +1108,9 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[UIManager flushUIBlocks]", (@{ @"count": [@(previousPendingUIBlocks.count) stringValue], })); - @try { - for (RCTViewManagerUIBlock block in previousPendingUIBlocks) { - block(self, self->_viewRegistry); - } - } - @catch (NSException *exception) { - RCTLogError(@"Exception thrown while executing UI block: %@", exception); - } + + mountingBlock(); + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); RCTExecuteOnUIManagerQueue(completion); diff --git a/React/Modules/RCTUIManagerObserverCoordinator.h b/React/Modules/RCTUIManagerObserverCoordinator.h index 83478b602..fc4064027 100644 --- a/React/Modules/RCTUIManagerObserverCoordinator.h +++ b/React/Modules/RCTUIManagerObserverCoordinator.h @@ -9,6 +9,8 @@ #import +typedef dispatch_block_t RCTUIManagerMountingBlock; + /** * Allows hooking into UIManager internals. This can be used to execute code at * specific points during the view updating process. @@ -43,6 +45,13 @@ */ - (void)uiManagerWillPerformMounting:(RCTUIManager *)manager; +/** + * Called right before flushing UI blocks and allows to intercept the mounting process. + * Return `YES` to cancel default execution of the `block` (and perform the + * execution later). + */ +- (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block; + /** * Called just after flushing UI blocks. * This is called from the UIManager queue. diff --git a/React/Modules/RCTUIManagerObserverCoordinator.mm b/React/Modules/RCTUIManagerObserverCoordinator.mm index 90e0255c9..a8fa5f6db 100644 --- a/React/Modules/RCTUIManagerObserverCoordinator.mm +++ b/React/Modules/RCTUIManagerObserverCoordinator.mm @@ -72,6 +72,20 @@ } } +- (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block +{ + std::lock_guard lock(_mutex); + + for (id observer in _observers) { + if ([observer respondsToSelector:@selector(uiManager:performMountingWithBlock:)]) { + if ([observer uiManager:manager performMountingWithBlock:block]) { + return YES; + } + } + } + return NO; +} + - (void)uiManagerDidPerformMounting:(RCTUIManager *)manager { std::lock_guard lock(_mutex);