From 00f44248b3c35c78ed4c0b67fbd8d91da8a86b41 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 10 Apr 2018 16:36:59 -0700 Subject: [PATCH] Fabric: RCTFabricSurface as separate class Summary: RCTFabricSurface was reimplemented as separate class which does not rely on (old) RCTUIManager and delegate some functionality (which must be coordinated between Surface instances and Scheduler) to RCTSurfacePresenter. Reviewed By: mdvacca Differential Revision: D7526404 fbshipit-source-id: e8c38261bc489fd6066ba29a829cd8f623c26217 --- React/Fabric/Surface/RCTFabricSurface.h | 119 +++++++- React/Fabric/Surface/RCTFabricSurface.mm | 256 +++++++++++++++++- .../RCTFabricSurfaceHostingProxyRootView.mm | 2 +- .../Surface/RCTFabricSurfaceHostingView.mm | 6 +- 4 files changed, 369 insertions(+), 14 deletions(-) diff --git a/React/Fabric/Surface/RCTFabricSurface.h b/React/Fabric/Surface/RCTFabricSurface.h index 1672a9a86..498fe1807 100644 --- a/React/Fabric/Surface/RCTFabricSurface.h +++ b/React/Fabric/Surface/RCTFabricSurface.h @@ -5,12 +5,123 @@ * LICENSE file in the root directory of this source tree. */ -#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RCTBridge; +@class RCTSurfaceView; +@class RCTSurfacePresenter; +@protocol RCTSurfaceDelegate; /** - * Fabric-compatible RCTSurface implementation. - * It may not continue extending from RCTSurface in the future. + * (This is Fabric-compatible RCTSurface implementation.) + * + * RCTSurface instance represents React Native-powered piece of a user interface + * which can be a full-screen app, separate modal view controller, + * or even small widget. + * It is called "Surface". + * + * The RCTSurface instance is completely thread-safe by design; + * it can be created on any thread, and any its method can be called from + * any thread (if the opposite is not mentioned explicitly). + * + * The primary goals of the RCTSurface are: + * * ability to measure and layout the surface in a thread-safe + * and synchronous manner; + * * ability to create a UIView instance on demand (later); + * * ability to communicate the current stage of the surface granularly. */ -@interface RCTFabricSurface : RCTSurface +@interface RCTFabricSurface : NSObject + +@property (atomic, readonly) RCTSurfaceStage stage; +@property (atomic, readonly) RCTBridge *bridge; +@property (atomic, readonly) NSString *moduleName; +@property (atomic, readonly) NSNumber *rootViewTag; + +@property (atomic, readwrite, weak, nullable) id delegate; + +@property (atomic, copy, readwrite) NSDictionary *properties; + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties; + +- (instancetype)initWithSurfacePresenter:(RCTSurfacePresenter *)surfacePresenter + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties; + +#pragma mark - Dealing with UIView representation, the Main thread only access + +/** + * Creates (if needed) and returns `UIView` instance which represents the Surface. + * The Surface will cache and *retain* this object. + * Returning the UIView instance does not mean that the Surface is ready + * to execute and layout. It can be just a handler which Surface will use later + * to mount the actual views. + * RCTSurface does not control (or influence in any way) the size or origin + * of this view. Some superview (or another owner) must use other methods + * of this class to setup proper layout and interop interactions with UIKit + * or another UI framework. + * This method must be called only from the main queue. + */ +- (RCTSurfaceView *)view; + +#pragma mark - Layout: Setting the size constrains + +/** + * Sets `minimumSize` and `maximumSize` layout constraints for the Surface. + */ +- (void)setMinimumSize:(CGSize)minimumSize + maximumSize:(CGSize)maximumSize; + +/** + * Previously set `minimumSize` layout constraint. + * Defaults to `{0, 0}`. + */ +@property (atomic, assign, readonly) CGSize minimumSize; + +/** + * Previously set `maximumSize` layout constraint. + * Defaults to `{CGFLOAT_MAX, CGFLOAT_MAX}`. + */ +@property (atomic, assign, readonly) CGSize maximumSize; + +/** + * Simple shortcut to `-[RCTSurface setMinimumSize:size maximumSize:size]`. + */ +- (void)setSize:(CGSize)size; + +#pragma mark - Layout: Measuring + +/** + * Measures the Surface with given constraints. + * This method does not cause any side effects on the surface object. + */ +- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize + maximumSize:(CGSize)maximumSize; + +/** + * Return the current size of the root view based on (but not clamp by) current + * size constraints. + */ +@property (atomic, assign, readonly) CGSize intrinsicSize; + +#pragma mark - Synchronous waiting + +/** + * Synchronously blocks the current thread up to given `timeout` until + * the Surface reaches `stage`. + * NOT SUPPORTED IN FABRIC YET. + */ +- (BOOL)synchronouslyWaitForStage:(RCTSurfaceStage)stage timeout:(NSTimeInterval)timeout; @end + +@interface RCTFabricSurface (Internal) + +- (void)_setStage:(RCTSurfaceStage)stage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Fabric/Surface/RCTFabricSurface.mm b/React/Fabric/Surface/RCTFabricSurface.mm index bcdaac131..babdc28c7 100644 --- a/React/Fabric/Surface/RCTFabricSurface.mm +++ b/React/Fabric/Surface/RCTFabricSurface.mm @@ -7,16 +7,260 @@ #import "RCTFabricSurface.h" +#import + +#import +#import + +#import #import +#import +#import +#import +#import +#import +#import -@implementation RCTFabricSurface +#import "RCTSurfacePresenter.h" +#import "RCTMountingManager.h" -- (void)unmountReactComponentWithBridge:(RCTBridge *)bridge rootViewTag:(NSNumber *)rootViewTag +@implementation RCTFabricSurface { + // Immutable + RCTSurfacePresenter *_surfacePresenter; + NSString *_moduleName; + ReactTag _rootTag; + + // Protected by the `_mutex` + std::mutex _mutex; + RCTSurfaceStage _stage; + NSDictionary *_properties; + CGSize _minimumSize; + CGSize _maximumSize; + CGSize _intrinsicSize; + + // The Main thread only + RCTSurfaceView *_Nullable _view; + RCTTouchHandler *_Nullable _touchHandler; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties { - [bridge enqueueJSCall:@"ReactFabric" - method:@"unmountComponentAtNodeAndRemoveContainer" - args:@[rootViewTag] - completion:NULL]; + RCTAssert(bridge.valid, @"Valid bridge is required to instanciate `RCTSurface`."); + + self = [self initWithSurfacePresenter:bridge.surfacePresenter + moduleName:moduleName + initialProperties:initialProperties]; + + return self; +} + +- (instancetype)initWithSurfacePresenter:(RCTSurfacePresenter *)surfacePresenter + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties +{ + + if (self = [super init]) { + _surfacePresenter = surfacePresenter; + _moduleName = moduleName; + _properties = [initialProperties copy]; + _rootTag = [RCTAllocateRootViewTag() integerValue]; + + _minimumSize = CGSizeZero; + _maximumSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX); + + _stage = RCTSurfaceStageSurfaceDidInitialize; + + [self _run]; + } + + return self; +} + +- (void)dealloc +{ + [self _stop]; +} + +#pragma mark - Immutable Properties (no need to enforce synchonization) + +- (NSString *)moduleName +{ + return _moduleName; +} + +- (NSNumber *)rootViewTag +{ + return @(_rootTag); +} + +#pragma mark - Main-Threaded Routines + +- (RCTSurfaceView *)view +{ + RCTAssertMainQueue(); + + if (!_view) { + _view = [[RCTSurfaceView alloc] initWithSurface:(RCTSurface *)self]; + } + + return _view; +} + +#pragma mark - Stage management + +- (RCTSurfaceStage)stage +{ + std::lock_guard lock(_mutex); + return _stage; +} + +- (void)_setStage:(RCTSurfaceStage)stage +{ + RCTSurfaceStage updatedStage; + { + std::lock_guard lock(_mutex); + + if (_stage & stage) { + return; + } + + updatedStage = (RCTSurfaceStage)(_stage | stage); + _stage = updatedStage; + } + + [self _propagateStageChange:updatedStage]; +} + +- (void)_propagateStageChange:(RCTSurfaceStage)stage +{ + // Updating the `view` + RCTExecuteOnMainQueue(^{ + self->_view.stage = stage; + }); + + // Notifying the `delegate` + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(surface:didChangeStage:)]) { + [delegate surface:(RCTSurface *)self didChangeStage:stage]; + } +} + +#pragma mark - Properties Management + +- (NSDictionary *)properties +{ + std::lock_guard lock(_mutex); + return _properties; +} + +- (void)setProperties:(NSDictionary *)properties +{ + { + std::lock_guard lock(_mutex); + + if ([properties isEqualToDictionary:_properties]) { + return; + } + + _properties = [properties copy]; + } + + [self _run]; +} + +#pragma mark - Running + +- (void)_run +{ + [_surfacePresenter registerSurface:self]; + [self _setStage:RCTSurfaceStageSurfaceDidRun]; +} + +- (void)_stop +{ + [_surfacePresenter unregisterSurface:self]; + [self _setStage:RCTSurfaceStageSurfaceDidStop]; +} + +#pragma mark - Layout + +- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize + maximumSize:(CGSize)maximumSize +{ + // TODO: Not supported yet. + return CGSizeZero; +} + +#pragma mark - Size Constraints + +- (void)setSize:(CGSize)size +{ + [self setMinimumSize:size maximumSize:size]; +} + +- (void)setMinimumSize:(CGSize)minimumSize + maximumSize:(CGSize)maximumSize +{ + { + std::lock_guard lock(_mutex); + if (CGSizeEqualToSize(minimumSize, _minimumSize) && + CGSizeEqualToSize(maximumSize, _maximumSize)) { + return; + } + + _maximumSize = maximumSize; + _minimumSize = minimumSize; + } + + // TODO: Not supported yet. +} + +- (CGSize)minimumSize +{ + std::lock_guard lock(_mutex); + return _minimumSize; +} + +- (CGSize)maximumSize +{ + std::lock_guard lock(_mutex); + return _maximumSize; +} + +#pragma mark - intrinsicSize + +- (void)setIntrinsicSize:(CGSize)intrinsicSize +{ + { + std::lock_guard lock(_mutex); + if (CGSizeEqualToSize(intrinsicSize, _intrinsicSize)) { + return; + } + + _intrinsicSize = intrinsicSize; + } + + // Notifying `delegate` + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(surface:didChangeIntrinsicSize:)]) { + [delegate surface:(RCTSurface *)(id)self didChangeIntrinsicSize:intrinsicSize]; + } +} + +- (CGSize)intrinsicSize +{ + std::lock_guard lock(_mutex); + return _intrinsicSize; +} + +#pragma mark - Synchronous Waiting + +- (BOOL)synchronouslyWaitForStage:(RCTSurfaceStage)stage timeout:(NSTimeInterval)timeout +{ + // TODO: Not supported yet. + return NO; } @end diff --git a/React/Fabric/Surface/RCTFabricSurfaceHostingProxyRootView.mm b/React/Fabric/Surface/RCTFabricSurfaceHostingProxyRootView.mm index 159a63c86..e2210daf8 100644 --- a/React/Fabric/Surface/RCTFabricSurfaceHostingProxyRootView.mm +++ b/React/Fabric/Surface/RCTFabricSurfaceHostingProxyRootView.mm @@ -13,7 +13,7 @@ - (RCTSurface *)createSurfaceWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties { - return [[RCTFabricSurface alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties]; + return (RCTSurface *)[[RCTFabricSurface alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties]; } @end diff --git a/React/Fabric/Surface/RCTFabricSurfaceHostingView.mm b/React/Fabric/Surface/RCTFabricSurfaceHostingView.mm index eec159cce..0157c974b 100644 --- a/React/Fabric/Surface/RCTFabricSurfaceHostingView.mm +++ b/React/Fabric/Surface/RCTFabricSurfaceHostingView.mm @@ -16,9 +16,9 @@ initialProperties:(NSDictionary *)initialProperties sizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode { - RCTFabricSurface *surface = [[RCTFabricSurface alloc] initWithBridge:bridge - moduleName:moduleName - initialProperties:initialProperties]; + RCTSurface *surface = (RCTSurface *)[[RCTFabricSurface alloc] initWithBridge:bridge + moduleName:moduleName + initialProperties:initialProperties]; return [self initWithSurface:surface sizeMeasureMode:sizeMeasureMode]; }