From 7df58e23a3a265b0df0edc753cc79a153fec90d8 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 7 Nov 2017 16:10:55 -0800 Subject: [PATCH] Introducing RCTSurface, new experimental thread-safe interop layer Summary: 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. Differential Revision: D6202576 fbshipit-source-id: 8e644c87fcaad2b6a9c9304b58384d7192747556 --- React/Base/Surface/RCTSurface.h | 119 +++++ React/Base/Surface/RCTSurface.mm | 482 ++++++++++++++++++ React/Base/Surface/RCTSurfaceDelegate.h | 36 ++ React/Base/Surface/RCTSurfaceRootShadowView.h | 41 ++ React/Base/Surface/RCTSurfaceRootShadowView.m | 138 +++++ .../RCTSurfaceRootShadowViewDelegate.h | 24 + React/Base/Surface/RCTSurfaceRootView.h | 23 + React/Base/Surface/RCTSurfaceRootView.mm | 18 + React/Base/Surface/RCTSurfaceStage.h | 24 + React/Base/Surface/RCTSurfaceView+Internal.h | 26 + React/Base/Surface/RCTSurfaceView.h | 30 ++ React/Base/Surface/RCTSurfaceView.mm | 115 +++++ React/Modules/RCTUIManager.h | 6 + React/Modules/RCTUIManager.m | 39 +- React/React.xcodeproj/project.pbxproj | 80 +++ 15 files changed, 1198 insertions(+), 3 deletions(-) create mode 100644 React/Base/Surface/RCTSurface.h create mode 100644 React/Base/Surface/RCTSurface.mm create mode 100644 React/Base/Surface/RCTSurfaceDelegate.h create mode 100644 React/Base/Surface/RCTSurfaceRootShadowView.h create mode 100644 React/Base/Surface/RCTSurfaceRootShadowView.m create mode 100644 React/Base/Surface/RCTSurfaceRootShadowViewDelegate.h create mode 100644 React/Base/Surface/RCTSurfaceRootView.h create mode 100644 React/Base/Surface/RCTSurfaceRootView.mm create mode 100644 React/Base/Surface/RCTSurfaceStage.h create mode 100644 React/Base/Surface/RCTSurfaceView+Internal.h create mode 100644 React/Base/Surface/RCTSurfaceView.h create mode 100644 React/Base/Surface/RCTSurfaceView.mm diff --git a/React/Base/Surface/RCTSurface.h b/React/Base/Surface/RCTSurface.h new file mode 100644 index 00000000000000..1f6ec2b63fea82 --- /dev/null +++ b/React/Base/Surface/RCTSurface.h @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RCTBridge; +@class RCTSurfaceView; +@protocol RCTSurfaceDelegate; + +/** + * 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 RCTSurface : 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 NS_DESIGNATED_INITIALIZER; + +#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 `{INFINITY, INFINITY}`. + */ +@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 will not have given `stage`. + * Do nothing, if called from the main or `UIManager` queue. + */ +- (BOOL)synchronouslyWaitForStage:(RCTSurfaceStage)stage timeout:(NSTimeInterval)timeout; + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Base/Surface/RCTSurface.mm b/React/Base/Surface/RCTSurface.mm new file mode 100644 index 00000000000000..5884f84beb4c80 --- /dev/null +++ b/React/Base/Surface/RCTSurface.mm @@ -0,0 +1,482 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTSurface.h" +#import "RCTSurfaceView+Internal.h" + +#import + +#import "RCTAssert.h" +#import "RCTBridge+Private.h" +#import "RCTBridge.h" +#import "RCTSurfaceDelegate.h" +#import "RCTSurfaceRootShadowView.h" +#import "RCTSurfaceRootShadowViewDelegate.h" +#import "RCTSurfaceRootView.h" +#import "RCTSurfaceView.h" +#import "RCTTouchHandler.h" +#import "RCTUIManager.h" +#import "RCTUIManagerUtils.h" + +@interface RCTSurface () +@end + +@implementation RCTSurface { + // Immutable + RCTBridge *_bridge; + NSString *_moduleName; + NSNumber *_rootViewTag; + + // Protected by the `_mutex` + std::mutex _mutex; + RCTBridge *_batchedBridge; + RCTSurfaceStage _stage; + NSDictionary *_properties; + CGSize _minimumSize; + CGSize _maximumSize; + CGSize _intrinsicSize; + + // The Main thread only + RCTSurfaceView *_Nullable _view; + RCTTouchHandler *_Nullable _touchHandler; + + // Semaphores + dispatch_semaphore_t _rootShadowViewDidStartRenderingSemaphore; + dispatch_semaphore_t _rootShadowViewDidStartLayingOutSemaphore; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties +{ + RCTAssert(bridge.valid, @"Valid bridge is required to instanciate `RCTSurface`."); + + if (self = [super init]) { + _bridge = bridge; + _batchedBridge = [_bridge batchedBridge] ?: _bridge; + _moduleName = moduleName; + _properties = [initialProperties copy]; + _rootViewTag = RCTAllocateRootViewTag(); + _rootShadowViewDidStartRenderingSemaphore = dispatch_semaphore_create(0); + _rootShadowViewDidStartLayingOutSemaphore = dispatch_semaphore_create(0); + + _minimumSize = CGSizeZero; + _maximumSize = CGSizeMake(INFINITY, INFINITY); + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleBridgeWillLoadJavaScriptNotification:) + name:RCTJavaScriptWillStartLoadingNotification + object:_bridge]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleBridgeDidLoadJavaScriptNotification:) + name:RCTJavaScriptDidLoadNotification + object:_bridge]; + + _stage = RCTSurfaceStageSurfaceDidInitialize; + + if (!bridge.loading) { + _stage = (RCTSurfaceStage)(_stage | RCTSurfaceStageBridgeDidLoad); + } + + [self _registerRootViewTag]; + [self _run]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Immutable Properties (no need to enforce synchonization) + +- (RCTBridge *)bridge +{ + return _bridge; +} + +- (NSString *)moduleName +{ + return _moduleName; +} + +- (NSNumber *)rootViewTag +{ + return _rootViewTag; +} + +#pragma mark - Convinience Internal Thread-Safe Properties + +- (RCTBridge *)_batchedBridge +{ + std::lock_guard lock(_mutex); + return _batchedBridge; +} + +- (RCTUIManager *)_uiManager +{ + return self._batchedBridge.uiManager; +} + +#pragma mark - Main-Threaded Routines + +- (RCTSurfaceView *)view +{ + RCTAssertMainQueue(); + + if (!_view) { + _view = [[RCTSurfaceView alloc] initWithSurface:self]; + + _touchHandler = [[RCTTouchHandler alloc] initWithBridge:self.bridge]; + [_touchHandler attachToView:_view]; + + [self _mountRootViewIfNeeded]; + } + + return _view; +} + +- (void)_mountRootViewIfNeeded +{ + RCTAssertMainQueue(); + + RCTSurfaceView *view = self->_view; + if (!view) { + return; + } + + RCTSurfaceRootView *rootView = + (RCTSurfaceRootView *)[self._uiManager viewForReactTag:self->_rootViewTag]; + if (!rootView) { + return; + } + + RCTAssert([rootView isKindOfClass:[RCTSurfaceRootView class]], + @"Received root view is not an instanse of `RCTSurfaceRootView`."); + + if (rootView.superview != view) { + view.rootView = rootView; + } +} + +#pragma mark - Bridge Events + +- (void)handleBridgeWillLoadJavaScriptNotification:(NSNotification *)notification +{ + RCTAssertMainQueue(); + + [self _setStage:RCTSurfaceStageBridgeDidLoad]; +} + +- (void)handleBridgeDidLoadJavaScriptNotification:(NSNotification *)notification +{ + RCTAssertMainQueue(); + + [self _setStage:RCTSurfaceStageModuleDidLoad]; + + RCTBridge *bridge = notification.userInfo[@"bridge"]; + + BOOL isRerunNeeded = NO; + + { + std::lock_guard lock(_mutex); + + if (bridge != _batchedBridge) { + _batchedBridge = bridge; + isRerunNeeded = YES; + } + } + + if (isRerunNeeded) { + [self _run]; + } +} + +#pragma mark - Stage management + +- (RCTSurfaceStage)stage +{ + std::lock_guard lock(_mutex); + return _stage; +} + +- (void)_setStage:(RCTSurfaceStage)stage +{ + { + std::lock_guard lock(_mutex); + + if (_stage & stage) { + return; + } + + _stage = (RCTSurfaceStage)(_stage | stage); + } + + [self _propagateStageChange:stage]; +} + +- (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: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 +{ + RCTBridge *batchedBridge; + NSDictionary *properties; + + { + std::lock_guard lock(_mutex); + + batchedBridge = _batchedBridge; + properties = _properties; + } + + if (!batchedBridge.valid) { + return; + } + + NSDictionary *applicationParameters = + @{ + @"rootTag": _rootViewTag, + @"initialProps": properties, + }; + + RCTLogInfo(@"Running surface %@ (%@)", _moduleName, applicationParameters); + + [batchedBridge enqueueJSCall:@"AppRegistry" + method:@"runApplication" + args:@[_moduleName, applicationParameters] + completion:NULL]; + + [self _setStage:RCTSurfaceStageSurfaceDidRun]; +} + +- (void)_registerRootViewTag +{ + RCTBridge *batchedBridge; + CGSize minimumSize; + CGSize maximumSize; + + { + std::lock_guard lock(_mutex); + batchedBridge = _batchedBridge; + minimumSize = _minimumSize; + maximumSize = _maximumSize; + } + + RCTUIManager *uiManager = batchedBridge.uiManager; + RCTUnsafeExecuteOnUIManagerQueueSync(^{ + [uiManager registerRootViewTag:self->_rootViewTag]; + + RCTSurfaceRootShadowView *rootShadowView = + (RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag]; + RCTAssert([rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]], + @"Received shadow view is not an instanse of `RCTSurfaceRootShadowView`."); + + [rootShadowView setMinimumSize:minimumSize + maximumSize:maximumSize]; + rootShadowView.delegate = self; + }); +} + +#pragma mark - Layout + +- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize + maximumSize:(CGSize)maximumSize +{ + RCTUIManager *uiManager = self._uiManager; + __block CGSize fittingSize; + + RCTUnsafeExecuteOnUIManagerQueueSync(^{ + RCTSurfaceRootShadowView *rootShadowView = + (RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag]; + + RCTAssert([rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]], + @"Received shadow view is not an instanse of `RCTSurfaceRootShadowView`."); + + fittingSize = [rootShadowView sizeThatFitsMinimumSize:minimumSize + maximumSize:maximumSize]; + }); + + return fittingSize; +} + +#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; + } + + RCTUIManager *uiManager = self._uiManager; + + RCTUnsafeExecuteOnUIManagerQueueSync(^{ + RCTSurfaceRootShadowView *rootShadowView = + (RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag]; + RCTAssert([rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]], + @"Received shadow view is not an instanse of `RCTSurfaceRootShadowView`."); + + [rootShadowView setMinimumSize:minimumSize maximumSize:maximumSize]; + [uiManager setNeedsLayout]; + }); +} + +- (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:self didChangeIntrinsicSize:intrinsicSize]; + } +} + +- (CGSize)intrinsicSize +{ + std::lock_guard lock(_mutex); + return _intrinsicSize; +} + +#pragma mark - Synchronous Waiting + +- (BOOL)synchronouslyWaitForStage:(RCTSurfaceStage)stage timeout:(NSTimeInterval)timeout +{ + dispatch_semaphore_t semaphore; + switch (stage) { + case RCTSurfaceStageSurfaceDidInitialLayout: + semaphore = _rootShadowViewDidStartLayingOutSemaphore; + break; + case RCTSurfaceStageSurfaceDidInitialRendering: + semaphore = _rootShadowViewDidStartRenderingSemaphore; + break; + default: + RCTAssert(NO, @"Only waiting for `RCTSurfaceStageSurfaceDidInitialRendering` and `RCTSurfaceStageSurfaceDidInitialLayout` stages is supported."); + } + + if (RCTIsMainQueue()) { + RCTLogInfo(@"Synchronous waiting is not supported on the main queue."); + return NO; + } + + if (RCTIsUIManagerQueue()) { + RCTLogInfo(@"Synchronous waiting is not supported on UIManager queue."); + return NO; + } + + BOOL timeoutOccured = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC)); + if (!timeoutOccured) { + // Balancing the semaphore. + // Note: `dispatch_semaphore_wait` reverts the decrement in case when timeout occured. + dispatch_semaphore_signal(semaphore); + } + + return !timeoutOccured; +} + +#pragma mark - RCTSurfaceRootShadowViewDelegate + +- (void)rootShadowView:(RCTRootShadowView *)rootShadowView didChangeIntrinsicSize:(CGSize)intrinsicSize +{ + self.intrinsicSize = intrinsicSize; +} + +- (void)rootShadowViewDidStartRendering:(RCTSurfaceRootShadowView *)rootShadowView +{ + [self _setStage:RCTSurfaceStageSurfaceDidInitialRendering]; + + dispatch_semaphore_signal(_rootShadowViewDidStartRenderingSemaphore); +} + +- (void)rootShadowViewDidStartLayingOut:(RCTSurfaceRootShadowView *)rootShadowView +{ + [self _setStage:RCTSurfaceStageSurfaceDidInitialLayout]; + + dispatch_semaphore_signal(_rootShadowViewDidStartLayingOutSemaphore); + + RCTExecuteOnMainQueue(^{ + // Rendering is happening, let's mount `rootView` into `view` if we already didn't do this. + [self _mountRootViewIfNeeded]; + }); +} + +@end diff --git a/React/Base/Surface/RCTSurfaceDelegate.h b/React/Base/Surface/RCTSurfaceDelegate.h new file mode 100644 index 00000000000000..5455320feb57af --- /dev/null +++ b/React/Base/Surface/RCTSurfaceDelegate.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RCTSurface; + +@protocol RCTSurfaceDelegate + +@optional + +/** + * Notifies a receiver that a surface transitioned to a new stage. + * See `RCTSurfaceStage` for more details. + */ +- (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage; + +/** + * Notifies a receiver that root view got a new (intrinsic) size during the last + * layout pass. + */ +- (void)surface:(RCTSurface *)surface didChangeIntrinsicSize:(CGSize)intrinsicSize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Base/Surface/RCTSurfaceRootShadowView.h b/React/Base/Surface/RCTSurfaceRootShadowView.h new file mode 100644 index 00000000000000..f681e67eb0374d --- /dev/null +++ b/React/Base/Surface/RCTSurfaceRootShadowView.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import +#import + +@interface RCTSurfaceRootShadowView : RCTShadowView + +@property (nonatomic, assign, readonly) CGSize minimumSize; +@property (nonatomic, assign, readonly) CGSize maximumSize; + +- (void)setMinimumSize:(CGSize)size maximumSize:(CGSize)maximumSize; + +@property (nonatomic, assign, readonly) CGSize intrinsicSize; + +@property (nonatomic, weak) id delegate; + +- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize + maximumSize:(CGSize)maximumSize; + +/** + * Layout direction (LTR or RTL) inherited from native environment and + * is using as a base direction value in layout engine. + * Defaults to value inferred from current locale. + */ +@property (nonatomic, assign) YGDirection baseDirection; + +/** + * Calculate all views whose frame needs updating after layout has been calculated. + * Returns a set contains the shadowviews that need updating. + */ +- (NSSet *)collectViewsWithUpdatedFrames; + +@end diff --git a/React/Base/Surface/RCTSurfaceRootShadowView.m b/React/Base/Surface/RCTSurfaceRootShadowView.m new file mode 100644 index 00000000000000..f076c9daeea136 --- /dev/null +++ b/React/Base/Surface/RCTSurfaceRootShadowView.m @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTSurfaceRootShadowView.h" + +#import + +#import "RCTI18nUtil.h" + +@implementation RCTSurfaceRootShadowView { + CGSize _intrinsicSize; + BOOL _isRendered; + BOOL _isLaidOut; +} + +- (instancetype)init +{ + if (self = [super init]) { + self.viewName = @"RCTSurfaceRootView"; + _baseDirection = [[RCTI18nUtil sharedInstance] isRTL] ? YGDirectionRTL : YGDirectionLTR; + _minimumSize = CGSizeZero; + _maximumSize = CGSizeMake(INFINITY, INFINITY); + + self.alignSelf = YGAlignStretch; + self.flex = 1; + } + + return self; +} + +- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex +{ + [super insertReactSubview:subview atIndex:atIndex]; + if (!_isRendered) { + [_delegate rootShadowViewDidStartRendering:self]; + _isRendered = YES; + } +} + +- (void)calculateLayoutWithMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximimSize +{ + // Treating `INFINITY` as `YGUndefined` (which equals `NAN`). + float availableWidth = isinf(maximimSize.width) ? YGUndefined : maximimSize.width; + float availableHeight = isinf(maximimSize.height) ? YGUndefined : maximimSize.height; + + self.minWidth = (YGValue){isinf(minimumSize.width) ? YGUndefined : minimumSize.width, YGUnitPoint}; + self.minWidth = (YGValue){isinf(minimumSize.height) ? YGUndefined : minimumSize.height, YGUnitPoint}; + + YGNodeCalculateLayout(self.yogaNode, availableWidth, availableHeight, _baseDirection); +} + +- (NSSet *)collectViewsWithUpdatedFrames +{ + [self calculateLayoutWithMinimumSize:_minimumSize + maximumSize:_maximumSize]; + + NSMutableSet *viewsWithNewFrame = [NSMutableSet set]; + [self applyLayoutNode:self.yogaNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero]; + + self.intrinsicSize = self.frame.size; + + if (_isRendered && !_isLaidOut) { + [_delegate rootShadowViewDidStartLayingOut:self]; + _isLaidOut = YES; + } + + return viewsWithNewFrame; +} + +- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize + maximumSize:(CGSize)maximumSize +{ + // Positive case where requested constraind are aready enforced. + if (CGSizeEqualToSize(minimumSize, _minimumSize) && + CGSizeEqualToSize(maximumSize, _maximumSize)) { + // We stil need to call `calculateLayoutWithMinimumSize:maximumSize` + // mehtod though. + [self calculateLayoutWithMinimumSize:_minimumSize + maximumSize:_maximumSize]; + + YGNodeRef yogaNode = self.yogaNode; + return CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); + } + + // Generic case, where requested constraind are different from enforced. + + // Applying given size constraints. + [self calculateLayoutWithMinimumSize:minimumSize + maximumSize:maximumSize]; + + YGNodeRef yogaNode = self.yogaNode; + CGSize fittingSize = + CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); + + // Reverting size constraints. + [self calculateLayoutWithMinimumSize:_minimumSize + maximumSize:_maximumSize]; + + return CGSizeMake( + MAX(minimumSize.width, MIN(maximumSize.width, fittingSize.width)), + MAX(minimumSize.height, MIN(maximumSize.height, fittingSize.height)) + ); +} + +- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize +{ + if (CGSizeEqualToSize(minimumSize, _minimumSize) && + CGSizeEqualToSize(maximumSize, _maximumSize)) { + return; + } + + _maximumSize = maximumSize; + _minimumSize = minimumSize; +} + +- (void)setIntrinsicSize:(CGSize)intrinsicSize +{ + if (CGSizeEqualToSize(_intrinsicSize, intrinsicSize)) { + return; + } + + _intrinsicSize = intrinsicSize; + + [_delegate rootShadowView:self didChangeIntrinsicSize:intrinsicSize]; +} + +- (CGSize)intrinsicSize +{ + return _intrinsicSize; +} + +@end diff --git a/React/Base/Surface/RCTSurfaceRootShadowViewDelegate.h b/React/Base/Surface/RCTSurfaceRootShadowViewDelegate.h new file mode 100644 index 00000000000000..1cfa6aecbcd587 --- /dev/null +++ b/React/Base/Surface/RCTSurfaceRootShadowViewDelegate.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RCTSurfaceRootShadowView; + +@protocol RCTSurfaceRootShadowViewDelegate + +- (void)rootShadowView:(RCTSurfaceRootShadowView *)rootShadowView didChangeIntrinsicSize:(CGSize)instrinsicSize; +- (void)rootShadowViewDidStartRendering:(RCTSurfaceRootShadowView *)rootShadowView; +- (void)rootShadowViewDidStartLayingOut:(RCTSurfaceRootShadowView *)rootShadowView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Base/Surface/RCTSurfaceRootView.h b/React/Base/Surface/RCTSurfaceRootView.h new file mode 100644 index 00000000000000..927563a51f9718 --- /dev/null +++ b/React/Base/Surface/RCTSurfaceRootView.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Internal class represents Surface's root view. + */ +@interface RCTSurfaceRootView : RCTView + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Base/Surface/RCTSurfaceRootView.mm b/React/Base/Surface/RCTSurfaceRootView.mm new file mode 100644 index 00000000000000..5888b18867a6c8 --- /dev/null +++ b/React/Base/Surface/RCTSurfaceRootView.mm @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTSurfaceRootView.h" + +#import "RCTDefines.h" + +@implementation RCTSurfaceRootView + +RCT_NOT_IMPLEMENTED(- (nullable instancetype)initWithCoder:(NSCoder *)coder) + +@end diff --git a/React/Base/Surface/RCTSurfaceStage.h b/React/Base/Surface/RCTSurfaceStage.h new file mode 100644 index 00000000000000..a02ebc36d80db8 --- /dev/null +++ b/React/Base/Surface/RCTSurfaceStage.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +/** + * The stage of the Surface + */ +typedef NS_OPTIONS(NSInteger, RCTSurfaceStage) { + RCTSurfaceStageSurfaceDidInitialize = 1 << 0, // Surface object was created + RCTSurfaceStageBridgeDidLoad = 1 << 1, // Bridge was loaded + RCTSurfaceStageModuleDidLoad = 1 << 2, // Module (JavaScript code) was loaded + RCTSurfaceStageSurfaceDidRun = 1 << 3, // Module (JavaScript code) was run + RCTSurfaceStageSurfaceDidInitialRendering = 1 << 4, // UIManager created the first shadow views + RCTSurfaceStageSurfaceDidInitialLayout = 1 << 5, // UIManager completed the first layout pass + RCTSurfaceStageSurfaceDidInitialMounting = 1 << 6, // UIManager completed the first mounting pass + RCTSurfaceStageSurfaceDidInvalidate = 1 << 7, // Surface received `invalidate` message +}; diff --git a/React/Base/Surface/RCTSurfaceView+Internal.h b/React/Base/Surface/RCTSurfaceView+Internal.h new file mode 100644 index 00000000000000..b41bd3a34a2fe5 --- /dev/null +++ b/React/Base/Surface/RCTSurfaceView+Internal.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import + +@class RCTSurfaceRootView; + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTSurfaceView (Internal) + +@property (nonatomic, strong) RCTSurfaceRootView *rootView; +@property (nonatomic, assign) RCTSurfaceStage stage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Base/Surface/RCTSurfaceView.h b/React/Base/Surface/RCTSurfaceView.h new file mode 100644 index 00000000000000..7f90e188e68daa --- /dev/null +++ b/React/Base/Surface/RCTSurfaceView.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RCTSurface; + +typedef UIView *(^RCTSurfaceActivityIndicatorViewFactory)(); + +/** + * UIView instance which represents the Surface + */ +@interface RCTSurfaceView : UIView + +- (instancetype)initWithSurface:(RCTSurface *)surface NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, copy, nullable) RCTSurfaceActivityIndicatorViewFactory activityIndicatorViewFactory; +@property (nonatomic, weak, readonly, nullable) RCTSurface *surface; + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Base/Surface/RCTSurfaceView.mm b/React/Base/Surface/RCTSurfaceView.mm new file mode 100644 index 00000000000000..ce3b59645c4514 --- /dev/null +++ b/React/Base/Surface/RCTSurfaceView.mm @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTSurfaceView.h" +#import "RCTSurfaceView+Internal.h" + +#import "RCTDefines.h" +#import "RCTSurface.h" +#import "RCTSurfaceRootView.h" + +@implementation RCTSurfaceView { + RCTSurfaceRootView *_Nullable _rootView; + UIView *_Nullable _activityIndicatorView; + RCTSurfaceStage _stage; +} + +RCT_NOT_IMPLEMENTED(- (instancetype)init) +RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) +RCT_NOT_IMPLEMENTED(- (nullable instancetype)initWithCoder:(NSCoder *)coder) + +- (instancetype)initWithSurface:(RCTSurface *)surface +{ + if (self = [super initWithFrame:CGRectZero]) { + _stage = surface.stage; + _surface = surface; + } + + return self; +} + +#pragma mark - Internal Interface + +- (void)setRootView:(RCTSurfaceRootView *)rootView +{ + if (_rootView == rootView) { + return; + } + + [_rootView removeFromSuperview]; + _rootView = rootView; + [self updateStage]; +} + +- (RCTSurfaceRootView *)rootView +{ + return _rootView; +} + +#pragma mark - activityIndicatorView + +- (void)setActivityIndicatorView:(UIView *)view +{ + [_activityIndicatorView removeFromSuperview]; + _activityIndicatorView = view; + _activityIndicatorView.frame = self.bounds; + _activityIndicatorView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:_activityIndicatorView]; +} + +- (UIView *)activityIndicatorView +{ + return _activityIndicatorView; +} + +#pragma mark - stage + +- (void)setStage:(RCTSurfaceStage)stage +{ + if (stage == _stage) { + return; + } + + _stage = stage; + + [self updateStage]; +} + +- (RCTSurfaceStage)stage +{ + return _stage; +} + +#pragma mark - Visibility + +- (void)updateStage +{ + BOOL displayRootView = _stage & RCTSurfaceStageSurfaceDidInitialLayout; + BOOL displayActivityIndicator = !displayRootView; + + if (displayRootView) { + if (_rootView.superview != self) { + [self addSubview:_rootView]; + } + } + else { + [_rootView removeFromSuperview]; + } + + if (displayActivityIndicator) { + if (!_activityIndicatorView && self.activityIndicatorViewFactory != nil) { + self.activityIndicatorView = self.activityIndicatorViewFactory(); + } + } + else { + [_activityIndicatorView removeFromSuperview]; + } +} + +@end diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index 237a109238adaf..35e41555668d1d 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -29,6 +29,12 @@ RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplier */ @interface RCTUIManager : NSObject +/** + * Register a root view tag and creates corresponding `rootView` and + * `rootShadowView`. + */ +- (void)registerRootViewTag:(NSNumber *)rootTag; + /** * Register a root view with the RCTUIManager. */ diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 5984c972ed32ec..dc88c187c01234 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -32,6 +32,8 @@ #import "RCTScrollableProtocol.h" #import "RCTShadowView+Internal.h" #import "RCTShadowView.h" +#import "RCTSurfaceRootShadowView.h" +#import "RCTSurfaceRootView.h" #import "RCTUIManagerObserverCoordinator.h" #import "RCTUIManagerUtils.h" #import "RCTUtils.h" @@ -96,7 +98,10 @@ - (void)invalidate RCTExecuteOnMainQueue(^{ RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"UIManager invalidate", nil); for (NSNumber *rootViewTag in self->_rootViewTags) { - [(id)self->_viewRegistry[rootViewTag] invalidate]; + UIView *rootView = self->_viewRegistry[rootViewTag]; + if ([rootView conformsToProtocol:@protocol(RCTInvalidating)]) { + [(id)rootView invalidate]; + } } self->_rootViewTags = nil; @@ -246,6 +251,31 @@ - (dispatch_queue_t)methodQueue return RCTGetUIManagerQueue(); } +- (void)registerRootViewTag:(NSNumber *)rootTag +{ + RCTAssertUIManagerQueue(); + + RCTAssert(RCTIsReactRootView(rootTag), + @"Attempt to register rootTag (%@) which is not actually root tag.", rootTag); + + RCTAssert(![_rootViewTags containsObject:rootTag], + @"Attempt to register rootTag (%@) which was already registred.", rootTag); + + [_rootViewTags addObject:rootTag]; + + // Registering root shadow view + RCTSurfaceRootShadowView *shadowView = [RCTSurfaceRootShadowView new]; + shadowView.reactTag = rootTag; + _shadowViewRegistry[rootTag] = shadowView; + + // Registering root view + RCTExecuteOnMainQueue(^{ + RCTSurfaceRootView *rootView = [RCTSurfaceRootView new]; + rootView.reactTag = rootTag; + self->_viewRegistry[rootTag] = rootView; + }); +} + - (void)registerRootView:(RCTRootContentView *)rootView { RCTAssertMainQueue(); @@ -551,7 +581,9 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView * RCTAssert(view != nil, @"view (for ID %@) not found", reactTag); RCTRootView *rootView = (RCTRootView *)[view superview]; - rootView.intrinsicContentSize = contentSize; + if ([rootView isKindOfClass:[RCTRootView class]]) { + rootView.intrinsicContentSize = contentSize; + } }); } } @@ -948,7 +980,8 @@ - (void)_manageChildren:(NSNumber *)containerTag [componentData setProps:props forShadowView:shadowView]; _shadowViewRegistry[reactTag] = shadowView; RCTShadowView *rootView = _shadowViewRegistry[rootTag]; - RCTAssert([rootView isKindOfClass:[RCTRootShadowView class]], + RCTAssert([rootView isKindOfClass:[RCTRootShadowView class]] || + [rootView isKindOfClass:[RCTSurfaceRootShadowView class]], @"Given `rootTag` (%@) does not correspond to a valid root shadow view instance.", rootTag); shadowView.rootView = (RCTRootShadowView *)rootView; } diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index d1c9a9c50a5942..6e3ab54b1e7bac 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -1030,6 +1030,30 @@ 598FD1951F817335006C54CB /* RCTModalManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 91076A881F743AB00081B4FA /* RCTModalManager.h */; }; 598FD1961F817335006C54CB /* RCTModalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 91076A871F743AB00081B4FA /* RCTModalManager.m */; }; 598FD1971F817336006C54CB /* RCTModalManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 91076A881F743AB00081B4FA /* RCTModalManager.h */; }; + 599FAA361FB274980058CCF6 /* RCTSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2A1FB274970058CCF6 /* RCTSurface.h */; }; + 599FAA371FB274980058CCF6 /* RCTSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2A1FB274970058CCF6 /* RCTSurface.h */; }; + 599FAA381FB274980058CCF6 /* RCTSurface.mm in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA2B1FB274970058CCF6 /* RCTSurface.mm */; }; + 599FAA391FB274980058CCF6 /* RCTSurface.mm in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA2B1FB274970058CCF6 /* RCTSurface.mm */; }; + 599FAA3A1FB274980058CCF6 /* RCTSurfaceDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2C1FB274970058CCF6 /* RCTSurfaceDelegate.h */; }; + 599FAA3B1FB274980058CCF6 /* RCTSurfaceDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2C1FB274970058CCF6 /* RCTSurfaceDelegate.h */; }; + 599FAA3C1FB274980058CCF6 /* RCTSurfaceRootShadowView.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2D1FB274970058CCF6 /* RCTSurfaceRootShadowView.h */; }; + 599FAA3D1FB274980058CCF6 /* RCTSurfaceRootShadowView.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2D1FB274970058CCF6 /* RCTSurfaceRootShadowView.h */; }; + 599FAA3E1FB274980058CCF6 /* RCTSurfaceRootShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA2E1FB274970058CCF6 /* RCTSurfaceRootShadowView.m */; }; + 599FAA3F1FB274980058CCF6 /* RCTSurfaceRootShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA2E1FB274970058CCF6 /* RCTSurfaceRootShadowView.m */; }; + 599FAA401FB274980058CCF6 /* RCTSurfaceRootShadowViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2F1FB274970058CCF6 /* RCTSurfaceRootShadowViewDelegate.h */; }; + 599FAA411FB274980058CCF6 /* RCTSurfaceRootShadowViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA2F1FB274970058CCF6 /* RCTSurfaceRootShadowViewDelegate.h */; }; + 599FAA421FB274980058CCF6 /* RCTSurfaceRootView.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA301FB274970058CCF6 /* RCTSurfaceRootView.h */; }; + 599FAA431FB274980058CCF6 /* RCTSurfaceRootView.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA301FB274970058CCF6 /* RCTSurfaceRootView.h */; }; + 599FAA441FB274980058CCF6 /* RCTSurfaceRootView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA311FB274970058CCF6 /* RCTSurfaceRootView.mm */; }; + 599FAA451FB274980058CCF6 /* RCTSurfaceRootView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA311FB274970058CCF6 /* RCTSurfaceRootView.mm */; }; + 599FAA461FB274980058CCF6 /* RCTSurfaceStage.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA321FB274970058CCF6 /* RCTSurfaceStage.h */; }; + 599FAA471FB274980058CCF6 /* RCTSurfaceStage.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA321FB274970058CCF6 /* RCTSurfaceStage.h */; }; + 599FAA481FB274980058CCF6 /* RCTSurfaceView+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA331FB274970058CCF6 /* RCTSurfaceView+Internal.h */; }; + 599FAA491FB274980058CCF6 /* RCTSurfaceView+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA331FB274970058CCF6 /* RCTSurfaceView+Internal.h */; }; + 599FAA4A1FB274980058CCF6 /* RCTSurfaceView.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA341FB274970058CCF6 /* RCTSurfaceView.h */; }; + 599FAA4B1FB274980058CCF6 /* RCTSurfaceView.h in Headers */ = {isa = PBXBuildFile; fileRef = 599FAA341FB274970058CCF6 /* RCTSurfaceView.h */; }; + 599FAA4C1FB274980058CCF6 /* RCTSurfaceView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA351FB274970058CCF6 /* RCTSurfaceView.mm */; }; + 599FAA4D1FB274980058CCF6 /* RCTSurfaceView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 599FAA351FB274970058CCF6 /* RCTSurfaceView.mm */; }; 59A7B9FD1E577DBF0068EDBF /* RCTRootContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 59A7B9FB1E577DBF0068EDBF /* RCTRootContentView.h */; }; 59A7B9FE1E577DBF0068EDBF /* RCTRootContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A7B9FC1E577DBF0068EDBF /* RCTRootContentView.m */; }; 59B1EBC91EBD46250047B19B /* RCTShadowView+Layout.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 590D7BFB1EBD458B00D8A370 /* RCTShadowView+Layout.h */; }; @@ -2073,6 +2097,18 @@ 5960C1B41F0804A00066FD5B /* RCTLayoutAnimationGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLayoutAnimationGroup.m; sourceTree = ""; }; 597633341F4E021D005BE8A4 /* RCTShadowView+Internal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTShadowView+Internal.m"; sourceTree = ""; }; 597633351F4E021D005BE8A4 /* RCTShadowView+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTShadowView+Internal.h"; sourceTree = ""; }; + 599FAA2A1FB274970058CCF6 /* RCTSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSurface.h; sourceTree = ""; }; + 599FAA2B1FB274970058CCF6 /* RCTSurface.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTSurface.mm; sourceTree = ""; }; + 599FAA2C1FB274970058CCF6 /* RCTSurfaceDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSurfaceDelegate.h; sourceTree = ""; }; + 599FAA2D1FB274970058CCF6 /* RCTSurfaceRootShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSurfaceRootShadowView.h; sourceTree = ""; }; + 599FAA2E1FB274970058CCF6 /* RCTSurfaceRootShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSurfaceRootShadowView.m; sourceTree = ""; }; + 599FAA2F1FB274970058CCF6 /* RCTSurfaceRootShadowViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSurfaceRootShadowViewDelegate.h; sourceTree = ""; }; + 599FAA301FB274970058CCF6 /* RCTSurfaceRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSurfaceRootView.h; sourceTree = ""; }; + 599FAA311FB274970058CCF6 /* RCTSurfaceRootView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTSurfaceRootView.mm; sourceTree = ""; }; + 599FAA321FB274970058CCF6 /* RCTSurfaceStage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSurfaceStage.h; sourceTree = ""; }; + 599FAA331FB274970058CCF6 /* RCTSurfaceView+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTSurfaceView+Internal.h"; sourceTree = ""; }; + 599FAA341FB274970058CCF6 /* RCTSurfaceView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSurfaceView.h; sourceTree = ""; }; + 599FAA351FB274970058CCF6 /* RCTSurfaceView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTSurfaceView.mm; sourceTree = ""; }; 59A7B9FB1E577DBF0068EDBF /* RCTRootContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootContentView.h; sourceTree = ""; }; 59A7B9FC1E577DBF0068EDBF /* RCTRootContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootContentView.m; sourceTree = ""; }; 59D031E51F8353D3008361F0 /* RCTSafeAreaShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSafeAreaShadowView.h; sourceTree = ""; }; @@ -2647,6 +2683,25 @@ path = "../third-party"; sourceTree = ""; }; + 599FAA291FB274970058CCF6 /* Surface */ = { + isa = PBXGroup; + children = ( + 599FAA2A1FB274970058CCF6 /* RCTSurface.h */, + 599FAA2B1FB274970058CCF6 /* RCTSurface.mm */, + 599FAA2C1FB274970058CCF6 /* RCTSurfaceDelegate.h */, + 599FAA2D1FB274970058CCF6 /* RCTSurfaceRootShadowView.h */, + 599FAA2E1FB274970058CCF6 /* RCTSurfaceRootShadowView.m */, + 599FAA2F1FB274970058CCF6 /* RCTSurfaceRootShadowViewDelegate.h */, + 599FAA301FB274970058CCF6 /* RCTSurfaceRootView.h */, + 599FAA311FB274970058CCF6 /* RCTSurfaceRootView.mm */, + 599FAA321FB274970058CCF6 /* RCTSurfaceStage.h */, + 599FAA331FB274970058CCF6 /* RCTSurfaceView+Internal.h */, + 599FAA341FB274970058CCF6 /* RCTSurfaceView.h */, + 599FAA351FB274970058CCF6 /* RCTSurfaceView.mm */, + ); + path = Surface; + sourceTree = ""; + }; 59D031E41F8353D3008361F0 /* SafeAreaView */ = { isa = PBXGroup; children = ( @@ -2803,6 +2858,7 @@ 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */, 83CBBA501A601E3B00E9B192 /* RCTUtils.m */, 199B8A6E1F44DB16005DEF67 /* RCTVersion.h */, + 599FAA291FB274970058CCF6 /* Surface */, ); path = Base; sourceTree = ""; @@ -2949,10 +3005,13 @@ 59D031F61F8353D3008361F0 /* RCTSafeAreaViewLocalData.h in Headers */, 3D302F3C1DF828F800D6DDAE /* RCTJavaScriptLoader.h in Headers */, 3D302F3D1DF828F800D6DDAE /* RCTJSStackFrame.h in Headers */, + 599FAA491FB274980058CCF6 /* RCTSurfaceView+Internal.h in Headers */, 3D302F3E1DF828F800D6DDAE /* RCTKeyCommands.h in Headers */, 135A9C031E7B0F6100587AEB /* RCTJSCErrorHandling.h in Headers */, + 599FAA411FB274980058CCF6 /* RCTSurfaceRootShadowViewDelegate.h in Headers */, 3D7BFD261EA8E351008DFB7A /* RCTSamplingProfilerPackagerMethod.h in Headers */, 3D302F3F1DF828F800D6DDAE /* RCTLog.h in Headers */, + 599FAA431FB274980058CCF6 /* RCTSurfaceRootView.h in Headers */, 3D302F401DF828F800D6DDAE /* RCTModuleData.h in Headers */, 5960C1B61F0804A00066FD5B /* RCTLayoutAnimation.h in Headers */, 3D302F411DF828F800D6DDAE /* RCTModuleMethod.h in Headers */, @@ -2988,6 +3047,8 @@ 3D302F5C1DF828F800D6DDAE /* RCTEventEmitter.h in Headers */, 652971261EE976BD003C8BD5 /* InspectorInterfaces.h in Headers */, 3D302F5D1DF828F800D6DDAE /* RCTExceptionsManager.h in Headers */, + 599FAA4B1FB274980058CCF6 /* RCTSurfaceView.h in Headers */, + 599FAA3D1FB274980058CCF6 /* RCTSurfaceRootShadowView.h in Headers */, 3D302F5E1DF828F800D6DDAE /* RCTI18nManager.h in Headers */, 3D302F5F1DF828F800D6DDAE /* RCTI18nUtil.h in Headers */, 3D302F601DF828F800D6DDAE /* RCTKeyboardObserver.h in Headers */, @@ -2997,6 +3058,7 @@ 3D302F631DF828F800D6DDAE /* RCTStatusBarManager.h in Headers */, 3D302F641DF828F800D6DDAE /* RCTTiming.h in Headers */, 3D302F651DF828F800D6DDAE /* RCTUIManager.h in Headers */, + 599FAA3B1FB274980058CCF6 /* RCTSurfaceDelegate.h in Headers */, 3D302F661DF828F800D6DDAE /* RCTFPSGraph.h in Headers */, 3D302F681DF828F800D6DDAE /* RCTMacros.h in Headers */, 3D302F691DF828F800D6DDAE /* RCTProfile.h in Headers */, @@ -3039,6 +3101,7 @@ 3D302F8A1DF828F800D6DDAE /* RCTScrollView.h in Headers */, CF2731C21E7B8DEF0044CA4F /* RCTDeviceInfo.h in Headers */, 3D302F8B1DF828F800D6DDAE /* RCTScrollViewManager.h in Headers */, + 599FAA371FB274980058CCF6 /* RCTSurface.h in Headers */, 3D302F8C1DF828F800D6DDAE /* RCTSegmentedControl.h in Headers */, 66CD94B21F1045E700CB3C7C /* RCTMaskedView.h in Headers */, 3D302F8D1DF828F800D6DDAE /* RCTSegmentedControlManager.h in Headers */, @@ -3057,6 +3120,7 @@ 3D302F9A1DF828F800D6DDAE /* RCTViewManager.h in Headers */, 3D302F9D1DF828F800D6DDAE /* RCTWrapperViewController.h in Headers */, 3D302F9F1DF828F800D6DDAE /* UIView+React.h in Headers */, + 599FAA471FB274980058CCF6 /* RCTSurfaceStage.h in Headers */, 13134CA11E296B2A00B9F3CB /* RCTCxxUtils.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3219,12 +3283,15 @@ 3D80DA211DF820620028D040 /* RCTBridge.h in Headers */, 13F880381E296D2800C3C7A1 /* JSCWrapper.h in Headers */, 3D80DA221DF820620028D040 /* RCTBridge+Private.h in Headers */, + 599FAA461FB274980058CCF6 /* RCTSurfaceStage.h in Headers */, 3D7BFD211EA8E351008DFB7A /* RCTReloadPackagerMethod.h in Headers */, + 599FAA361FB274980058CCF6 /* RCTSurface.h in Headers */, 3D80DA231DF820620028D040 /* RCTBridgeDelegate.h in Headers */, 3D80DA241DF820620028D040 /* RCTBridgeMethod.h in Headers */, 3D7BFD151EA8E351008DFB7A /* RCTPackagerClient.h in Headers */, 3D80DA251DF820620028D040 /* RCTBridgeModule.h in Headers */, 3D80DA261DF820620028D040 /* RCTBundleURLProvider.h in Headers */, + 599FAA401FB274980058CCF6 /* RCTSurfaceRootShadowViewDelegate.h in Headers */, 5960C1B51F0804A00066FD5B /* RCTLayoutAnimation.h in Headers */, 3D80DA271DF820620028D040 /* RCTConvert.h in Headers */, 3D80DA281DF820620028D040 /* RCTDefines.h in Headers */, @@ -3240,6 +3307,7 @@ 3D80DA2F1DF820620028D040 /* RCTInvalidating.h in Headers */, 598FD1971F817336006C54CB /* RCTModalManager.h in Headers */, 3D7BFD251EA8E351008DFB7A /* RCTSamplingProfilerPackagerMethod.h in Headers */, + 599FAA3A1FB274980058CCF6 /* RCTSurfaceDelegate.h in Headers */, 9936F3381F5F2F480010BF04 /* PrivateDataBase.h in Headers */, 3D80DA301DF820620028D040 /* RCTJavaScriptExecutor.h in Headers */, 135A9BFF1E7B0EE600587AEB /* RCTJSCHelpers.h in Headers */, @@ -3255,6 +3323,7 @@ 3D80DA381DF820620028D040 /* RCTMultipartStreamReader.h in Headers */, 3D80DA391DF820620028D040 /* RCTNullability.h in Headers */, 3D80DA3A1DF820620028D040 /* RCTParserUtils.h in Headers */, + 599FAA421FB274980058CCF6 /* RCTSurfaceRootView.h in Headers */, 59D031F91F8353D3008361F0 /* RCTSafeAreaViewManager.h in Headers */, 3D80DA3B1DF820620028D040 /* RCTPerformanceLogger.h in Headers */, 59D031F51F8353D3008361F0 /* RCTSafeAreaViewLocalData.h in Headers */, @@ -3343,6 +3412,7 @@ 3D80DA801DF820620028D040 /* RCTScrollViewManager.h in Headers */, 3D80DA811DF820620028D040 /* RCTSegmentedControl.h in Headers */, C6827DF61EF17CCC00D66BEF /* RCTJSEnvironment.h in Headers */, + 599FAA3C1FB274980058CCF6 /* RCTSurfaceRootShadowView.h in Headers */, 3D80DA821DF820620028D040 /* RCTSegmentedControlManager.h in Headers */, 3D80DA831DF820620028D040 /* RCTShadowView.h in Headers */, 3D80DA841DF820620028D040 /* RCTSlider.h in Headers */, @@ -3361,6 +3431,7 @@ 590D7BFD1EBD458B00D8A370 /* RCTShadowView+Layout.h in Headers */, 3D80DA8F1DF820620028D040 /* RCTViewManager.h in Headers */, 13134CA01E296B2A00B9F3CB /* RCTCxxUtils.h in Headers */, + 599FAA4A1FB274980058CCF6 /* RCTSurfaceView.h in Headers */, 3D80DA901DF820620028D040 /* RCTWebView.h in Headers */, 652971251EE976BA003C8BD5 /* InspectorInterfaces.h in Headers */, 3D80DA911DF820620028D040 /* RCTWebViewManager.h in Headers */, @@ -3368,6 +3439,7 @@ 3D80DA931DF820620028D040 /* UIView+Private.h in Headers */, 59FBEFB01E46D91C0095D885 /* RCTScrollContentShadowView.h in Headers */, 3D80DA941DF820620028D040 /* UIView+React.h in Headers */, + 599FAA481FB274980058CCF6 /* RCTSurfaceView+Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3860,6 +3932,7 @@ 2D3B5ECA1D9B095F00451313 /* RCTComponentData.m in Sources */, 2D3B5EA31D9B08BE00451313 /* RCTParserUtils.m in Sources */, 59500D461F71C63F00B122B7 /* RCTUIManagerUtils.m in Sources */, + 599FAA4D1FB274980058CCF6 /* RCTSurfaceView.mm in Sources */, 2D3B5EA01D9B08B200451313 /* RCTLog.mm in Sources */, 2D3B5EE21D9B09B400451313 /* RCTScrollViewManager.m in Sources */, 5960C1BC1F0804A00066FD5B /* RCTLayoutAnimationGroup.m in Sources */, @@ -3900,11 +3973,13 @@ 2D3B5EB61D9B091400451313 /* RCTExceptionsManager.m in Sources */, 2D3B5EEB1D9B09D000451313 /* RCTTabBarItem.m in Sources */, 2D3B5ED41D9B097D00451313 /* RCTModalHostView.m in Sources */, + 599FAA391FB274980058CCF6 /* RCTSurface.mm in Sources */, C606692F1F3CC60500E67165 /* RCTModuleMethod.mm in Sources */, 2D3B5E9F1D9B08AF00451313 /* RCTKeyCommands.m in Sources */, 2D3B5EA51D9B08C700451313 /* RCTRootView.m in Sources */, 13134C871E296B2A00B9F3CB /* RCTCxxBridge.mm in Sources */, CF2731C31E7B8DF30044CA4F /* RCTDeviceInfo.m in Sources */, + 599FAA3F1FB274980058CCF6 /* RCTSurfaceRootShadowView.m in Sources */, 597633371F4E021D005BE8A4 /* RCTShadowView+Internal.m in Sources */, 2D3B5EB11D9B090100451313 /* RCTAppState.m in Sources */, 1384E20B1E806D5B00545659 /* RCTNativeModule.mm in Sources */, @@ -3927,6 +4002,7 @@ 2D3B5EBD1D9B092A00451313 /* RCTTiming.m in Sources */, 135A9C041E7B0F6400587AEB /* RCTJSCErrorHandling.mm in Sources */, 2D3B5EA81D9B08D300451313 /* RCTUtils.m in Sources */, + 599FAA451FB274980058CCF6 /* RCTSurfaceRootView.mm in Sources */, 2D3B5EC81D9B095800451313 /* RCTActivityIndicatorViewManager.m in Sources */, 598FD1961F817335006C54CB /* RCTModalManager.m in Sources */, 3DCD185D1DF978E7007FE5A1 /* RCTReloadCommand.m in Sources */, @@ -4120,6 +4196,7 @@ 14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */, 13134C861E296B2A00B9F3CB /* RCTCxxBridge.mm in Sources */, 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */, + 599FAA4C1FB274980058CCF6 /* RCTSurfaceView.mm in Sources */, 3D7BFD231EA8E351008DFB7A /* RCTReloadPackagerMethod.m in Sources */, 352DCFF01D19F4C20056D623 /* RCTI18nUtil.m in Sources */, 66CD94B71F1045E700CB3C7C /* RCTMaskedViewManager.m in Sources */, @@ -4148,6 +4225,7 @@ 3D7BFD171EA8E351008DFB7A /* RCTPackagerClient.m in Sources */, 14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */, 13134C8E1E296B2A00B9F3CB /* RCTMessageThread.mm in Sources */, + 599FAA381FB274980058CCF6 /* RCTSurface.mm in Sources */, 59D031EF1F8353D3008361F0 /* RCTSafeAreaShadowView.m in Sources */, 3D1E68DB1CABD13900DD7465 /* RCTDisplayLink.m in Sources */, 14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */, @@ -4159,6 +4237,7 @@ C606692E1F3CC60500E67165 /* RCTModuleMethod.mm in Sources */, 1450FF8A1BCFF28A00208362 /* RCTProfileTrampoline-x86_64.S in Sources */, 13D9FEEB1CDCCECF00158BD7 /* RCTEventEmitter.m in Sources */, + 599FAA3E1FB274980058CCF6 /* RCTSurfaceRootShadowView.m in Sources */, AC70D2E91DE489E4002E6351 /* RCTJavaScriptLoader.mm in Sources */, 14F7A0EC1BDA3B3C003C6C10 /* RCTPerfMonitor.m in Sources */, 5960C1B71F0804A00066FD5B /* RCTLayoutAnimation.m in Sources */, @@ -4169,6 +4248,7 @@ 13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */, 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */, 369123E11DDC75850095B341 /* RCTJSCSamplingProfiler.m in Sources */, + 599FAA441FB274980058CCF6 /* RCTSurfaceRootView.mm in Sources */, C60669361F3CCF1B00E67165 /* RCTManagedPointer.mm in Sources */, 13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */, 13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */,