From 34b8876ac6510398e03a03c94f4ffb9aaa7519d3 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Fri, 2 Mar 2018 19:32:09 -0800 Subject: [PATCH] iOS: Introduced RCTSurfaceHostingProxyRootView for migration to RCTSurfaceHostingView Summary: To help with migration from direct usages of RCTRootView to RCTSurfaceHostingView, RCTSurfaceHostingProxyRootView is added, which is simply a custom impl of RCTSurfaceHostingView, but will all RCTRootView APIs preserved. This makes it easy to do a drop-in replacement in native callsites: ``` // before: RCTRootView *rootView = [[RCTRootView alloc] init...]; // after: RCTRootView *rootView = (RCTRootView *)[[RCTSurfaceHostingProxyRootView alloc] init...]; ``` Reviewed By: shergin Differential Revision: D7141696 fbshipit-source-id: db8c447749eaa896efaa37774a9babef132128eb --- .../RCTSurfaceHostingProxyRootView.h | 65 ++++++ .../RCTSurfaceHostingProxyRootView.mm | 205 ++++++++++++++++++ .../RCTSurfaceHostingView.h | 12 +- .../RCTSurfaceHostingView.mm | 20 +- 4 files changed, 295 insertions(+), 7 deletions(-) create mode 100644 React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.h create mode 100644 React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm diff --git a/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.h b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.h new file mode 100644 index 00000000000000..b761cc33bde868 --- /dev/null +++ b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.h @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +#import + +#import "RCTSurfaceHostingView.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This is a RCTRootView-compatible implementation of RCTSurfaceHostingView. + * Use this class to replace all usages of RCTRootView in the app for easier migration + * to RCTSurfaceHostingView. + * + * WARNING: In the future, RCTRootView will be deprecated in favor of RCTSurfaceHostingView. + */ +@interface RCTSurfaceHostingProxyRootView : RCTSurfaceHostingView + +#pragma mark RCTRootView compatibility - keep these sync'ed with RCTRootView.h + +@property (nonatomic, copy, readonly) NSString *moduleName; +@property (nonatomic, strong, readonly) RCTBridge *bridge; +@property (nonatomic, copy, readwrite) NSDictionary *appProperties; +@property (nonatomic, assign) RCTRootViewSizeFlexibility sizeFlexibility; +@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) UIViewController *reactViewController; +@property (nonatomic, strong, readonly) UIView *contentView; +@property (nonatomic, strong) UIView *loadingView; +@property (nonatomic, assign) BOOL passThroughTouches; +@property (nonatomic, assign) NSTimeInterval loadingViewFadeDelay; +@property (nonatomic, assign) NSTimeInterval loadingViewFadeDuration; +@property (nonatomic, assign) BOOL fabric; + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + fabric:(BOOL)fabric NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties; + +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + launchOptions:(NSDictionary *)launchOptions; + +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + launchOptions:(NSDictionary *)launchOptions + fabric:(BOOL)fabric; + +- (void)cancelTouches; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm new file mode 100644 index 00000000000000..114b1f3981bfc3 --- /dev/null +++ b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTSurfaceHostingProxyRootView.h" + +#import + +#import "RCTAssert.h" +#import "RCTBridge.h" +#import "RCTLog.h" +#import "RCTPerformanceLogger.h" +#import "RCTProfile.h" +#import "RCTRootContentView.h" +#import "RCTRootViewDelegate.h" +#import "RCTSurface.h" +#import "UIView+React.h" + +static RCTSurfaceSizeMeasureMode convertToSurfaceSizeMeasureMode(RCTRootViewSizeFlexibility sizeFlexibility) { + switch (sizeFlexibility) { + case RCTRootViewSizeFlexibilityWidthAndHeight: + return RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightUndefined; + case RCTRootViewSizeFlexibilityWidth: + return RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightExact; + case RCTRootViewSizeFlexibilityHeight: + return RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightUndefined; + case RCTRootViewSizeFlexibilityNone: + return RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact; + } +} + +static RCTRootViewSizeFlexibility convertToRootViewSizeFlexibility(RCTSurfaceSizeMeasureMode sizeMeasureMode) { + switch (sizeMeasureMode) { + case RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightUndefined: + return RCTRootViewSizeFlexibilityWidthAndHeight; + case RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightExact: + return RCTRootViewSizeFlexibilityWidth; + case RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightUndefined: + return RCTRootViewSizeFlexibilityHeight; + case RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact: + default: + return RCTRootViewSizeFlexibilityNone; + } +} + +@implementation RCTSurfaceHostingProxyRootView + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + fabric:(BOOL)fabric +{ + RCTAssertMainQueue(); + RCTAssert(bridge, @"A bridge instance is required to create an RCTSurfaceHostingProxyRootView"); + RCTAssert(moduleName, @"A moduleName is required to create an RCTSurfaceHostingProxyRootView"); + + RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTSurfaceHostingProxyRootView init]", nil); + if (!bridge.isLoading) { + [bridge.performanceLogger markStartForTag:RCTPLTTI]; + } + + if (self = [super initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties fabric:fabric]) { + self.backgroundColor = [UIColor whiteColor]; + } + + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); + + return self; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties +{ + return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties fabric:NO]; +} + +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + launchOptions:(NSDictionary *)launchOptions + fabric:(BOOL)fabric +{ + RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL + moduleProvider:nil + launchOptions:launchOptions]; + + return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties fabric:fabric]; +} + +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + launchOptions:(NSDictionary *)launchOptions +{ + return [self initWithBundleURL:bundleURL moduleName:moduleName initialProperties:initialProperties launchOptions:launchOptions fabric:NO]; +} + +RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) +RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) + +# pragma mark proxy methods to RCTSurfaceHostingView + +- (NSString *)moduleName +{ + return super.surface.moduleName; +} + +- (RCTBridge *)bridge +{ + return super.surface.bridge; +} + +- (BOOL)fabric +{ + return super.surface.fabric; +} + +- (UIView *)contentView +{ + return self; +} + +- (NSNumber *)reactTag +{ + return super.surface.rootViewTag; +} + +- (RCTRootViewSizeFlexibility)sizeFlexibility +{ + return convertToRootViewSizeFlexibility(super.sizeMeasureMode); +} + +- (void)setSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility +{ + super.sizeMeasureMode = convertToSurfaceSizeMeasureMode(sizeFlexibility); +} + +- (NSDictionary *)appProperties +{ + return super.surface.properties; +} + +- (void)setAppProperties:(NSDictionary *)appProperties +{ + [super.surface setProperties:appProperties]; +} + +- (CGSize)intrinsicContentSize +{ + return super.surface.intrinsicSize; +} + +- (UIView *)loadingView +{ + return super.activityIndicatorViewFactory ? super.activityIndicatorViewFactory() : nil; +} + +- (void)setLoadingView:(UIView *)loadingView +{ + super.activityIndicatorViewFactory = ^UIView *(void) { + return loadingView; + }; +} + +#pragma mark RCTSurfaceDelegate proxying + +- (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage +{ + [super surface:surface didChangeStage:stage]; + if (RCTSurfaceStageIsRunning(stage)) { + [super.surface.bridge.performanceLogger markStopForTag:RCTPLTTI]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:RCTContentDidAppearNotification + object:self]; + }); + } +} + +- (void)surface:(RCTSurface *)surface didChangeIntrinsicSize:(CGSize)intrinsicSize +{ + [super surface:surface didChangeIntrinsicSize:intrinsicSize]; + + [_delegate rootViewDidChangeIntrinsicSize:(RCTRootView *)self]; +} + +#pragma mark legacy + +- (UIViewController *)reactViewController +{ + return _reactViewController ?: [super reactViewController]; +} + +#pragma mark unsupported + +- (void)cancelTouches +{ + // Not supported. +} + +@end + diff --git a/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h index 340a7c33b1987d..6f6ffe50f599ac 100644 --- a/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h +++ b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h @@ -7,6 +7,7 @@ #import +#import #import #import @@ -23,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN * This class can be used as easy-to-use general purpose integration point * of ReactNative-powered experiences in UIKit based apps. */ -@interface RCTSurfaceHostingView : UIView +@interface RCTSurfaceHostingView : UIView /** * Designated initializer. @@ -41,6 +42,15 @@ NS_ASSUME_NONNULL_BEGIN moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties; +/** + * Convenience initializer. + * To control toggle Fabric for the Surface. + */ +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + fabric:(BOOL)fabric; + /** * Surface object which is currently using to power the view. * Read-only. diff --git a/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm index 3fd9048d128828..de4f5ec97332cb 100644 --- a/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm +++ b/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm @@ -13,7 +13,7 @@ #import "RCTSurfaceView.h" #import "RCTUtils.h" -@interface RCTSurfaceHostingView () +@interface RCTSurfaceHostingView () @property (nonatomic, assign) BOOL isActivityIndicatorViewVisible; @property (nonatomic, assign) BOOL isSurfaceViewVisible; @@ -33,13 +33,21 @@ @implementation RCTSurfaceHostingView { - (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties + fabric:(BOOL)fabric { - RCTSurface *surface = - [[RCTSurface alloc] initWithBridge:bridge - moduleName:moduleName - initialProperties:initialProperties]; - + RCTSurface *surface = [[RCTSurface alloc] initWithBridge:bridge + moduleName:moduleName + initialProperties:initialProperties + fabric:fabric]; return [self initWithSurface:surface]; + +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties +{ + return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties fabric:NO]; } - (instancetype)initWithSurface:(RCTSurface *)surface