diff --git a/React/Fabric/RCTSurfacePresenter.h b/React/Fabric/RCTSurfacePresenter.h index df68bcda4..746af8a28 100644 --- a/React/Fabric/RCTSurfacePresenter.h +++ b/React/Fabric/RCTSurfacePresenter.h @@ -37,6 +37,9 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)registerSurface:(RCTFabricSurface *)surface; - (void)unregisterSurface:(RCTFabricSurface *)surface; +- (void)setProps:(NSDictionary *)props + surface:(RCTFabricSurface *)surface; + - (nullable RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag; /** @@ -57,6 +60,9 @@ NS_ASSUME_NONNULL_BEGIN @interface RCTSurfacePresenter (Deprecated) +@property (nonatomic) std::function uiManagerInstaller; +@property (nonatomic) std::function uiManagerUninstaller; + /** * We need to expose `uiManager` for registration * purposes. Eventually, we will move this down to C++ side. diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index 59891c024..e164998f3 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -38,11 +38,12 @@ using namespace facebook::react; @end @implementation RCTSurfacePresenter { - RCTScheduler *_scheduler; + RCTScheduler *_Nullable _scheduler; RCTMountingManager *_mountingManager; RCTBridge *_bridge; RCTBridge *_batchedBridge; RCTSurfaceRegistry *_surfaceRegistry; + SharedContextContainer _contextContainer; } - (instancetype)initWithBridge:(RCTBridge *)bridge @@ -69,10 +70,10 @@ using namespace facebook::react; void *imageLoader = (__bridge void *)[[RCTBridge currentBridge] imageLoader]; contextContainer->registerInstance(std::make_shared(imageLoader)); - _scheduler = [[RCTScheduler alloc] initWithContextContainer:contextContainer]; - _scheduler.delegate = self; + _contextContainer = contextContainer; _surfaceRegistry = [[RCTSurfaceRegistry alloc] init]; + _mountingManager = [[RCTMountingManager alloc] init]; _mountingManager.delegate = self; @@ -94,18 +95,19 @@ using namespace facebook::react; [[NSNotificationCenter defaultCenter] removeObserver:self]; } -#pragma mark - RCTSchedulerDelegate - -- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations - rootTag:(ReactTag)rootTag +- (void)createSchedulerIfNeeded { - [_mountingManager performTransactionWithMutations:mutations - rootTag:rootTag]; + if (_scheduler) { + return; + } + + _scheduler = [[RCTScheduler alloc] initWithContextContainer:_contextContainer]; + _scheduler.delegate = self; } -- (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName +- (void)ensureSchedulerDoesExist { - [_mountingManager preliminaryCreateComponentViewWithName:componentName]; + RCTAssert(_scheduler, @"RCTSurfacePresenter: RCTScheduler instance must be already instantiated at this point."); } #pragma mark - Internal Surface-dedicated Interface @@ -113,20 +115,25 @@ using namespace facebook::react; - (void)registerSurface:(RCTFabricSurface *)surface { [_surfaceRegistry registerSurface:surface]; - [_scheduler registerRootTag:surface.rootTag]; - [self runSurface:surface]; - // FIXME: mutation MUST produce instruction for root node. - [_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag]; + [self startSurface:surface]; } - (void)unregisterSurface:(RCTFabricSurface *)surface { [self stopSurface:surface]; - [_scheduler unregisterRootTag:surface.rootTag]; + [_surfaceRegistry unregisterSurface:surface]; } +- (void)setProps:(NSDictionary *)props + surface:(RCTFabricSurface *)surface +{ + // This implementation is suboptimal indeed but still better than nothing for now. + [self stopSurface:surface]; + [self startSurface:surface]; +} + - (RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag { return [_surfaceRegistry surfaceForRootTag:rootTag]; @@ -136,6 +143,8 @@ using namespace facebook::react; maximumSize:(CGSize)maximumSize surface:(RCTFabricSurface *)surface { + [self ensureSchedulerDoesExist]; + LayoutContext layoutContext; layoutContext.pointScaleFactor = RCTScreenScale(); LayoutConstraints layoutConstraints = {}; @@ -151,6 +160,8 @@ using namespace facebook::react; maximumSize:(CGSize)maximumSize surface:(RCTFabricSurface *)surface { + [self ensureSchedulerDoesExist]; + LayoutContext layoutContext; layoutContext.pointScaleFactor = RCTScreenScale(); LayoutConstraints layoutConstraints = {}; @@ -162,53 +173,101 @@ using namespace facebook::react; rootTag:surface.rootTag]; } -- (void)runSurface:(RCTFabricSurface *)surface +- (void)startSurface:(RCTFabricSurface *)surface { + [_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag]; + + [self createSchedulerIfNeeded]; + [_scheduler registerRootTag:surface.rootTag]; + + [self setMinimumSize:surface.minimumSize + maximumSize:surface.maximumSize + surface:surface]; + + // TODO: Move this down to Scheduler. NSDictionary *applicationParameters = @{ @"rootTag": @(surface.rootTag), @"initialProps": surface.properties, }; - - [_batchedBridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[surface.moduleName, applicationParameters] completion:NULL]; + [self->_batchedBridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[surface.moduleName, applicationParameters] completion:NULL]; } - (void)stopSurface:(RCTFabricSurface *)surface { + // TODO: Move this down to Scheduler. [_batchedBridge enqueueJSCall:@"ReactFabric" method:@"unmountComponentAtNode" args:@[@(surface.rootTag)] completion:NULL]; + + [self ensureSchedulerDoesExist]; + [_scheduler unregisterRootTag:surface.rootTag]; + + UIView *rootView = [_mountingManager.componentViewRegistry componentViewByTag:surface.rootTag]; + [_mountingManager.componentViewRegistry enqueueComponentViewWithName:@"Root" tag:surface.rootTag componentView:rootView]; + + [surface _unsetStage:(RCTSurfaceStagePrepared | RCTSurfaceStageMounted)]; +} + +- (void)startAllSurfaces +{ + for (RCTFabricSurface *surface in _surfaceRegistry.enumerator) { + [self startSurface:surface]; + } +} + +- (void)stopAllSurfaces +{ + for (RCTFabricSurface *surface in _surfaceRegistry.enumerator) { + [self stopSurface:surface]; + } +} + +#pragma mark - RCTSchedulerDelegate + +- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations + rootTag:(ReactTag)rootTag +{ + RCTFabricSurface *surface = [_surfaceRegistry surfaceForRootTag:rootTag]; + + [surface _setStage:RCTSurfaceStagePrepared]; + + [_mountingManager performTransactionWithMutations:mutations + rootTag:rootTag]; +} + +- (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName +{ + [_mountingManager preliminaryCreateComponentViewWithName:componentName]; } #pragma mark - RCTMountingManagerDelegate - (void)mountingManager:(RCTMountingManager *)mountingManager willMountComponentsWithRootTag:(ReactTag)rootTag { - RCTIsMainQueue(); - // TODO: Propagate state change to Surface. + RCTAssertMainQueue(); + + // Does nothing. } - (void)mountingManager:(RCTMountingManager *)mountingManager didMountComponentsWithRootTag:(ReactTag)rootTag { - RCTIsMainQueue(); + RCTAssertMainQueue(); + RCTFabricSurface *surface = [_surfaceRegistry surfaceForRootTag:rootTag]; - - // FIXME: Implement proper state propagation mechanism. - [surface _setStage:RCTSurfaceStageSurfaceDidInitialRendering]; - [surface _setStage:RCTSurfaceStageSurfaceDidInitialLayout]; - [surface _setStage:RCTSurfaceStageSurfaceDidInitialMounting]; - - UIView *rootComponentView = [_mountingManager.componentViewRegistry componentViewByTag:rootTag]; - - surface.view.rootView = (RCTSurfaceRootView *)rootComponentView; + RCTSurfaceStage stage = surface.stage; + if (stage & RCTSurfaceStagePrepared) { + // We have to progress the stage only if the preparing phase is done. + if ([surface _setStage:RCTSurfaceStageMounted]) { + UIView *rootComponentView = [_mountingManager.componentViewRegistry componentViewByTag:rootTag]; + surface.view.rootView = (RCTSurfaceRootView *)rootComponentView; + } + } } #pragma mark - Bridge events - (void)handleBridgeWillReloadNotification:(NSNotification *)notification { - // TODO: Define a lifecycle contract for the pieces involved here including the scheduler, mounting manager, and - // the surface registry. For now simply recreate the scheduler on reload. - // The goal is to deallocate the Scheduler and its underlying references before the JS runtime is destroyed. - _scheduler = [[RCTScheduler alloc] init]; - _scheduler.delegate = self; + [self stopAllSurfaces]; + _scheduler = nil; } - (void)handleJavaScriptDidLoadNotification:(NSNotification *)notification @@ -216,6 +275,8 @@ using namespace facebook::react; RCTBridge *bridge = notification.userInfo[@"bridge"]; if (bridge != _batchedBridge) { _batchedBridge = bridge; + + [self startAllSurfaces]; } } @@ -233,6 +294,26 @@ using namespace facebook::react; return _bridge; } +- (void)setUiManagerInstaller:(std::function)uiManagerInstaller +{ + _contextContainer->registerInstance(uiManagerInstaller, "uimanager-installer"); +} + +- (std::function)uiManagerInstaller +{ + return _contextContainer->getInstance>("uimanager-installer"); +} + +- (void)setUiManagerUninstaller:(std::function)uiManagerUninstaller +{ + _contextContainer->registerInstance(uiManagerUninstaller, "uimanager-uninstaller"); +} + +- (std::function)uiManagerUninstaller +{ + return _contextContainer->getInstance>("uimanager-uninstaller"); +} + @end @implementation RCTBridge (RCTSurfacePresenter) diff --git a/React/Fabric/Surface/RCTFabricSurface.mm b/React/Fabric/Surface/RCTFabricSurface.mm index 597c2e790..ae68f1806 100644 --- a/React/Fabric/Surface/RCTFabricSurface.mm +++ b/React/Fabric/Surface/RCTFabricSurface.mm @@ -162,8 +162,7 @@ _properties = [properties copy]; } - // TODO: Implement this in RCTSurfacePresenter. - // [_surfacePresenter setProps:properties surface:self]; + [_surfacePresenter setProps:properties surface:self]; } #pragma mark - Layout