From 6ee70a9cae08ecc4bf2d292b0e07bd0e93df594e Mon Sep 17 00:00:00 2001 From: Paige Sun Date: Tue, 29 Mar 2022 11:52:49 -0700 Subject: [PATCH] 5/n Allow CKComponents to embed Fabric surfaces Summary: Changelog: [Fabric][iOS] Allow CKComponents to embed Fabric surfaces too. Previously RCTSurfaceHostingComponent, a CKComponent, could only initialize the legacy RCTSurface. Now it can initialize RCTFabricSurface too, when a RCTSurfacePresenter is passed in. Reviewed By: RSNara Differential Revision: D35163595 fbshipit-source-id: e11a9334b0282e0728a38cc1c96de48a694e9e3d --- BUCK | 2 ++ .../RCTSurfaceBackedComponent.h | 2 ++ .../RCTSurfaceBackedComponent.mm | 22 ++++++++++++++----- .../RCTSurfaceHostingComponent.mm | 15 +++++++++++-- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/BUCK b/BUCK index ea5bacbb89..d1efbebcce 100644 --- a/BUCK +++ b/BUCK @@ -1362,6 +1362,7 @@ rn_xplat_cxx_library2( visibility = ["PUBLIC"], deps = [ "//fbobjc/Libraries/MobileUI/ComponentKit:ComponentKit", + "//xplat/js/react-native-github:RCTFabric", "//xplat/js/react-native-github:RCTLinking", "//xplat/js/react-native-github:RCTPushNotification", "//xplat/js/react-native-github:ReactInternal", @@ -1397,6 +1398,7 @@ rn_xplat_cxx_library2( deps = [ ":RCTSurfaceHostingComponent", "//fbobjc/Libraries/MobileUI/ComponentKit:ComponentKit", + "//xplat/js/react-native-github:RCTFabric", "//xplat/js/react-native-github:RCTLinking", "//xplat/js/react-native-github:RCTPushNotification", "//xplat/js/react-native-github:ReactInternal", diff --git a/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.h b/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.h index d6cbaee832..63a9c96065 100644 --- a/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.h +++ b/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.h @@ -8,6 +8,7 @@ #import #import #import +#import @class RCTBridge; @@ -19,6 +20,7 @@ @interface RCTSurfaceBackedComponent : CKCompositeComponent + (instancetype)newWithBridge:(RCTBridge *)bridge + surfacePresenter:(RCTSurfacePresenter *)surfacePresenter moduleName:(NSString *)moduleName properties:(NSDictionary *)properties options:(RCTSurfaceHostingComponentOptions)options; diff --git a/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.mm b/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.mm index 50a3346d14..cb4c4ef27a 100644 --- a/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.mm +++ b/Libraries/SurfaceBackedComponent/RCTSurfaceBackedComponent.mm @@ -13,6 +13,7 @@ #import #import #import +#import #import "RCTSurfaceBackedComponentState.h" @@ -24,6 +25,7 @@ } + (instancetype)newWithBridge:(RCTBridge *)bridge + surfacePresenter:(RCTSurfacePresenter *)surfacePresenter moduleName:(NSString *)moduleName properties:(NSDictionary *)properties options:(RCTSurfaceHostingComponentOptions)options @@ -32,11 +34,21 @@ RCTSurfaceBackedComponentState *state = scope.state(); + // JavaScript entrypoints expects "fabric" key for Fabric surfaces + NSMutableDictionary *adjustedProperties = [[NSMutableDictionary alloc] initWithDictionary:properties]; + adjustedProperties[@"fabric"] = surfacePresenter ? @YES : nil; + if (state.surface == nil || ![state.surface.moduleName isEqualToString:moduleName]) { - id surface = [[RCTSurface alloc] initWithBridge:bridge + id surface; + if (surfacePresenter) { + surface = [[RCTFabricSurface alloc] initWithSurfacePresenter:surfacePresenter + moduleName:moduleName + initialProperties:adjustedProperties]; + } else { + surface = [[RCTSurface alloc] initWithBridge:bridge moduleName:moduleName - initialProperties:properties]; - + initialProperties:adjustedProperties]; + } [surface start]; state = [RCTSurfaceBackedComponentState newWithSurface:surface]; @@ -44,8 +56,8 @@ CKComponentScope::replaceState(scope, state); } else { - if (![state.surface.properties isEqualToDictionary:properties]) { - state.surface.properties = properties; + if (![state.surface.properties isEqualToDictionary:adjustedProperties]) { + state.surface.properties = adjustedProperties; } } diff --git a/Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponent.mm b/Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponent.mm index 41db84fe67..64e6c124e1 100644 --- a/Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponent.mm +++ b/Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponent.mm @@ -11,6 +11,7 @@ #import #import +#import #import #import @@ -77,10 +78,20 @@ // Just in case of the very first building pass, we give React Native a chance // to prepare its internals for coming synchronous measuring. if ([_surface isKindOfClass:[RCTSurface class]]) { + // Legacy Pre-Fabric Surface [(RCTSurface *)_surface synchronouslyWaitForStage:RCTSurfaceStageSurfaceDidInitialLayout - timeout:_options.synchronousLayoutingTimeout]; + timeout:_options.synchronousLayoutingTimeout]; + } else if ([_surface isKindOfClass:[RCTFabricSurface class]]) { + // Fabric Surface + // Hack: Increase timeout because RCTFabricSurface stage will be RCTSurfaceStageSurfaceDidInitialLayout + // before mounting has finished, which can cause sizeThatFitsMinimumSize to return the wrong value. + // Safe hack because timeout length can be increased without making the component seem slower. + // However if timeout length is less than the time to mount a surface, the size may be incorrect. + // TODO (T115399546) Allow RCTFabricSurface synchronouslyWaitFor to wait for mounting completion stage + NSTimeInterval timeout = 20; + [(RCTFabricSurface *)_surface synchronouslyWaitFor:timeout]; } - + CGSize fittingSize = CGSizeZero; if (_surface.stage & RCTSurfaceStageSurfaceDidInitialLayout) { fittingSize = [_surface sizeThatFitsMinimumSize:constrainedSize.min