From d9f5695b061a37e0e76ccbf78ced5ffa3da505be Mon Sep 17 00:00:00 2001 From: raycgwang Date: Thu, 12 Oct 2023 23:23:13 +0800 Subject: [PATCH] refactor(ios): Revert rename and sdk frame refactor part 13 HippyRootView and sdk start related change --- .../RenderPage/HippyDemoViewController.mm | 22 +- .../examples/ios-demo/HippyDemo/TestModule.mm | 2 +- framework/ios/base/bridge/HippyBridge.h | 10 +- framework/ios/base/bridge/HippyBridge.mm | 48 +- .../ios/base/executors/HippyJSExecutor.mm | 10 +- .../ios/base/modules/HippyModulesSetup.mm | 6 +- hippy_backup.podspec | 453 ++++++++++++++++++ modules/ios/base/HippyAsserts.m | 9 + modules/ios/base/HippyDefines.h | 11 + renderer/native/ios/renderer/HippyRootView.h | 118 ++++- renderer/native/ios/renderer/HippyRootView.m | 81 ---- renderer/native/ios/renderer/HippyRootView.mm | 399 +++++++++++++++ .../ios/renderer/HippyRootViewDelegate.h | 50 ++ 13 files changed, 1084 insertions(+), 135 deletions(-) create mode 100644 hippy_backup.podspec delete mode 100644 renderer/native/ios/renderer/HippyRootView.m create mode 100644 renderer/native/ios/renderer/HippyRootView.mm create mode 100644 renderer/native/ios/renderer/HippyRootViewDelegate.h diff --git a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm index a8fee04be42..fdf86d7de55 100644 --- a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm +++ b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm @@ -35,7 +35,7 @@ static NSString *const engineKey = @"Demo"; -@interface HippyDemoViewController () { +@interface HippyDemoViewController () { DriverType _driverType; RenderType _renderType; BOOL _isDebugMode; @@ -111,13 +111,13 @@ - (void)runHippyCache { - (void)runHippyDemo { NSDictionary *launchOptions = @{@"EnableTurbo": @(DEMO_ENABLE_TURBO), @"DebugMode": @(_isDebugMode)}; - NSString *executorKey = [NSString stringWithFormat:@"%@_%u", engineKey, arc4random()]; - + NSString *uniqueEngineKey = [NSString stringWithFormat:@"%@_%u", engineKey, arc4random()]; + _hippyBridge = [[HippyBridge alloc] initWithDelegate:self moduleProvider:nil launchOptions:launchOptions - executorKey:executorKey]; - _hippyBridge.contextName = executorKey; + engineKey:uniqueEngineKey]; + _hippyBridge.contextName = uniqueEngineKey; _hippyBridge.moduleName = @"Demo"; _hippyBridge.methodInterceptor = self; @@ -132,15 +132,17 @@ - (void)mountConnector:(HippyBridge *)hippyBridge { isSimulator = YES; #endif - HippyRootView *rootView = [[HippyRootView alloc] initWithFrame:self.contentAreaView.bounds]; + HippyRootView *rootView = [[HippyRootView alloc] initWithBridge:hippyBridge + moduleName:@"Demo" + initialProperties:@{@"isSimulator": @(isSimulator)} + delegate:self]; + rootView.frame = self.contentAreaView.bounds; rootView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - [hippyBridge setRootView:rootView]; - NSNumber *rootTag = [rootView hippyTag]; if (_isDebugMode) { hippyBridge.sandboxDirectory = [_debugURL URLByDeletingLastPathComponent]; [hippyBridge loadBundleURL:_debugURL completion:^(NSURL * _Nullable, NSError * _Nullable) { - [hippyBridge loadInstanceForRootView:rootTag withProperties:@{@"isSimulator": @(isSimulator)}]; + [rootView runHippyApplication]; }]; } else { NSURL *vendorBundleURL = [self vendorBundleURL]; @@ -151,7 +153,7 @@ - (void)mountConnector:(HippyBridge *)hippyBridge { hippyBridge.sandboxDirectory = [indexBundleURL URLByDeletingLastPathComponent]; [hippyBridge loadBundleURL:indexBundleURL completion:^(NSURL * _Nullable, NSError * _Nullable) { NSLog(@"url %@ load finish", indexBundleURL); - [hippyBridge loadInstanceForRootView:rootTag withProperties:@{@"isSimulator": @(isSimulator)}]; + [rootView runHippyApplication]; }]; } diff --git a/framework/examples/ios-demo/HippyDemo/TestModule.mm b/framework/examples/ios-demo/HippyDemo/TestModule.mm index d76134a9089..48f98b6d4d6 100644 --- a/framework/examples/ios-demo/HippyDemo/TestModule.mm +++ b/framework/examples/ios-demo/HippyDemo/TestModule.mm @@ -71,7 +71,7 @@ - (void)runCommonDemo:(nonnull NSString *)bundleUrl { _connector = [[HippyBridge alloc] initWithDelegate:self moduleProvider:nil launchOptions:launchOptions - executorKey:engineKey]; + engineKey:engineKey]; [_connector setInspectable:YES]; //set custom vfs loader _connector.sandboxDirectory = sandboxDirectory; diff --git a/framework/ios/base/bridge/HippyBridge.h b/framework/ios/base/bridge/HippyBridge.h index 82e84d92858..329a89c9e55 100644 --- a/framework/ios/base/bridge/HippyBridge.h +++ b/framework/ios/base/bridge/HippyBridge.h @@ -21,7 +21,6 @@ */ #import - #import "HippyBridgeDelegate.h" #import "HippyBridgeModule.h" #import "HippyMethodInterceptorProtocol.h" @@ -29,7 +28,6 @@ #import "HippyImageProviderProtocol.h" #import "HippyInvalidating.h" #import "HippyDefines.h" - #include @class HippyJSExecutor; @@ -98,13 +96,13 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); * @param delegate bridge delegate * @param block for user-defined module * @param launchOptions launch options, will not be sent to frontend - * @param executorKey key to engine instance. HippyBridge with same engine key will share same engine intance + * @param engineKey key to engine instance. HippyBridge with same engine key will share same engine intance * @return A HippyBridge instance */ - (instancetype)initWithDelegate:(id)delegate moduleProvider:(nullable HippyBridgeModuleProviderBlock)block - launchOptions:(NSDictionary *)launchOptions - executorKey:(NSString *)executorKey; + launchOptions:(nullable NSDictionary *)launchOptions + engineKey:(nullable NSString *)engineKey; /** * Context name for HippyBridge @@ -258,7 +256,7 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); @property (nonatomic, assign) BOOL debugMode; -@property (nonatomic, strong) NSString *appVerson; // +@property (nonatomic, strong) NSString *appVerson; @property (nonatomic, assign) HippyInvalidateReason invalidateReason; diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index 2eff74fb565..488b892210f 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -96,7 +96,6 @@ @interface HippyBridge() { BOOL _wasBatchActive; HippyDisplayLink *_displayLink; HippyBridgeModuleProviderBlock _moduleProvider; - NSString *_engineKey; BOOL _valid; HippyBundleOperationQueue *_bundlesQueue; NSMutableArray *_bundleURLs; @@ -109,6 +108,13 @@ @interface HippyBridge() { } +/// 用于标记bridge所使用的JS引擎的Key +/// +/// 注意:传入相同值的bridge将共享底层JS引擎。 +/// 在共享情况下,只有全部bridge实例均释放,JS引擎资源才会销毁。 +/// 默认情况下对每个bridge使用独立JS引擎 +@property (nonatomic, strong) NSString *engineKey; + @property(readwrite, strong) dispatch_semaphore_t moduleSemaphore; @property(readwrite, assign) NSInteger loadingCount; @@ -141,14 +147,14 @@ + (void)initialize { - (instancetype)initWithDelegate:(id)delegate moduleProvider:(HippyBridgeModuleProviderBlock)block launchOptions:(NSDictionary *)launchOptions - executorKey:(NSString *)executorKey { + engineKey:(NSString *)engineKey { if (self = [super init]) { _delegate = delegate; _moduleProvider = block; _bundleURLs = [NSMutableArray array]; _debugMode = [launchOptions[@"DebugMode"] boolValue]; _enableTurbo = !!launchOptions[@"EnableTurbo"] ? [launchOptions[@"EnableTurbo"] boolValue] : YES; - _engineKey = executorKey; + _engineKey = engineKey.length > 0 ? engineKey : [NSString stringWithFormat:@"%p", self]; _invalidateReason = HippyInvalidateReasonDealloc; _valid = YES; _bundlesQueue = [[HippyBundleOperationQueue alloc] init]; @@ -221,12 +227,8 @@ - (void)bindKeys { #endif } -- (NSString *)engineKey { - return _engineKey ?: [NSString stringWithFormat:@"%p", self]; -} - - (void)setUpNativeRenderManager { - auto engineResource = [[HippyJSEnginesMapper defaultInstance] JSEngineResourceForKey:[self engineKey]]; + auto engineResource = [[HippyJSEnginesMapper defaultInstance] JSEngineResourceForKey:self.engineKey]; auto domManager = engineResource->GetDomManager(); //Create NativeRenderManager auto nativeRenderManager = std::make_shared(); @@ -299,6 +301,9 @@ - (BOOL)moduleIsInitialized:(Class)moduleClass { return [_moduleSetup moduleIsInitialized:moduleClass]; } + +#pragma mark - Debug Reload + - (void)reload { if ([self.delegate respondsToSelector:@selector(reload:)]) { self.invalidateReason = HippyInvalidateReasonReload; @@ -316,13 +321,16 @@ - (void)requestReload { } } + +#pragma mark - Bridge SetUp + - (void)setUp { _valid = YES; self.moduleSemaphore = dispatch_semaphore_create(0); @try { __weak HippyBridge *weakSelf = self; _moduleSetup = [[HippyModulesSetup alloc] initWithBridge:self extraProviderModulesBlock:_moduleProvider]; - _javaScriptExecutor = [[HippyJSExecutor alloc] initWithEngineKey:_engineKey bridge:self]; + _javaScriptExecutor = [[HippyJSExecutor alloc] initWithEngineKey:self.engineKey bridge:self]; _javaScriptExecutor.contextCreatedBlock = ^(id ctxWrapper){ HippyBridge *strongSelf = weakSelf; if (strongSelf) { @@ -340,21 +348,19 @@ - (void)setUp { _javaScriptExecutor.contextName = _contextName; } _displayLink = [[HippyDisplayLink alloc] init]; - //The caller may attempt to look up a module immediately after creating the HippyBridge, - //therefore the initialization of all modules cannot be placed in a sub-thread -// dispatch_async(HippyBridgeQueue(), ^{ - [self initWithModulesCompletion:^{ + + // Setup all extra and internal modules + [_moduleSetup setupModulesCompletion:^{ HippyBridge *strongSelf = weakSelf; if (strongSelf) { dispatch_semaphore_signal(strongSelf.moduleSemaphore); footstone::TimePoint endTime = footstone::TimePoint::SystemNow(); - auto enty = - strongSelf.javaScriptExecutor.pScope->GetPerformance()->PerformanceNavigation("hippyInit"); + auto enty = strongSelf.javaScriptExecutor.pScope->GetPerformance()->PerformanceNavigation("hippyInit"); enty->SetHippyNativeInitStart(strongSelf->_startTime); enty->SetHippyNativeInitEnd(endTime); } }]; -// }); + } @catch (NSException *exception) { HippyBridgeHandleException(exception, self); } @@ -376,12 +382,6 @@ - (void)loadBundleURL:(NSURL *)bundleURL }); } - - -- (void)initWithModulesCompletion:(dispatch_block_t)completion { - [_moduleSetup setupModulesCompletion:completion]; -} - - (void)beginLoadingBundle:(NSURL *)bundleURL completion:(void (^)(NSURL * _Nullable, NSError * _Nullable))completion { dispatch_group_t group = dispatch_group_create(); @@ -1056,7 +1056,7 @@ - (void)setSnapShotData:(NSData *)data { //FIXME: 调整优化 - (void)setRootView:(HippyRootView *)rootView { - auto engineResource = [[HippyJSEnginesMapper defaultInstance] JSEngineResourceForKey:[self engineKey]]; + auto engineResource = [[HippyJSEnginesMapper defaultInstance] JSEngineResourceForKey:self.engineKey]; auto domManager = engineResource->GetDomManager(); NSNumber *rootTag = [rootView hippyTag]; //Create a RootNode instance with a root tag @@ -1090,7 +1090,7 @@ - (void)setRootView:(HippyRootView *)rootView { } - (void)resetRootSize:(CGSize)size { - auto engineResource = [[HippyJSEnginesMapper defaultInstance] JSEngineResourceForKey:[self engineKey]]; + auto engineResource = [[HippyJSEnginesMapper defaultInstance] JSEngineResourceForKey:self.engineKey]; std::weak_ptr rootNode = _rootNode; auto domManager = engineResource->GetDomManager(); std::weak_ptr weakDomManager = domManager; diff --git a/framework/ios/base/executors/HippyJSExecutor.mm b/framework/ios/base/executors/HippyJSExecutor.mm index 60992381712..4341f671f3e 100644 --- a/framework/ios/base/executors/HippyJSExecutor.mm +++ b/framework/ios/base/executors/HippyJSExecutor.mm @@ -233,11 +233,9 @@ - (void)setup { } - (instancetype)initWithEngineKey:(NSString *)engineKey bridge:(HippyBridge *)bridge { + NSParameterAssert(engineKey.length > 0); if (self = [super init]) { _valid = YES; - // maybe bug in JavaScriptCore: - // JSContextRef held by JSContextGroupRef cannot be deallocated, - // unless JSContextGroupRef is deallocated self.enginekey = engineKey; self.bridge = bridge; @@ -351,7 +349,7 @@ - (void)invalidate { #endif //JS_JSC self.pScope->WillExit(); self.pScope = nullptr; - NSString *enginekey = [self enginekey]; + NSString *enginekey = self.enginekey; if (!enginekey) { return; } @@ -360,10 +358,6 @@ - (void)invalidate { }); } -- (NSString *)enginekey { - return _enginekey ?: [NSString stringWithFormat:@"%p", self]; -} - - (void)setContextName:(NSString *)contextName { #ifdef JS_JSC if (!contextName) { diff --git a/framework/ios/base/modules/HippyModulesSetup.mm b/framework/ios/base/modules/HippyModulesSetup.mm index 5f6af28a4e3..79dc84cb8b2 100644 --- a/framework/ios/base/modules/HippyModulesSetup.mm +++ b/framework/ios/base/modules/HippyModulesSetup.mm @@ -169,8 +169,8 @@ - (void)setupModulesCompletion:(dispatch_block_t)completion { NSMutableArray *moduleDataByID = [NSMutableArray new]; NSMutableDictionary *moduleDataByName = [NSMutableDictionary new]; - for (id module in extraModules) { - Class moduleClass = [module class]; + for (id extraModule in extraModules) { + Class moduleClass = [extraModule class]; NSString *moduleName = HippyBridgeModuleNameForClass(moduleClass); if (HIPPY_DEBUG) { // Check for name collisions between preregistered modules @@ -183,7 +183,7 @@ - (void)setupModulesCompletion:(dispatch_block_t)completion { } } // Instantiate moduleData container - HippyModuleData *moduleData = [[HippyModuleData alloc] initWithModuleInstance:module bridge:_bridge]; + HippyModuleData *moduleData = [[HippyModuleData alloc] initWithModuleInstance:extraModule bridge:_bridge]; moduleDataByName[moduleName] = moduleData; [moduleClassesByID addObject:moduleClass]; [moduleDataByID addObject:moduleData]; diff --git a/hippy_backup.podspec b/hippy_backup.podspec new file mode 100644 index 00000000000..0e32cc620b9 --- /dev/null +++ b/hippy_backup.podspec @@ -0,0 +1,453 @@ +# +# Be sure to run `pod lib lint hippy.podspec --verbose --use-libraries' to ensure this is a +# valid spec before submitting. +# +# Any lines starting with a # are optional, but their use is encouraged +# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html +# + +layout_engine = "Taitank" +js_engine = "jsc" +use_frameworks = false; + +Pod::Spec.new do |s| + if ENV["layout_engine"] + layout_engine = ENV["layout_engine"] + end + if ENV["use_frameworks"] + use_frameworks = true + end + if ENV["js_engine"] + js_engine = ENV["js_engine"] + end + puts "layout engine is #{layout_engine}, js engine is #{js_engine}" + puts "use_frameworks trigger is #{use_frameworks}" + if use_frameworks + framework_header_path = '${PODS_CONFIGURATION_BUILD_DIR}/hippy/hippy.framework/Headers' + s.module_map = false; + end + + s.name = 'hippy' + s.version = '3.0.0' + s.summary = 'Hippy Cross Platform Framework' + s.description = <<-DESC + Hippy is designed for developers to easily build cross-platform and high-performance awesome apps. + DESC + s.homepage = 'https://hippyjs.org' + s.license = { :type => 'Apache2', :file => 'LICENSE' } + s.author = 'OpenHippy Team' + s.source = {:git => 'https://github.com/Tencent/Hippy.git', :tag => s.version} + s.platform = :ios + s.ios.deployment_target = '11.0' + + #prepare_command not working for subspecs,so we remove devtools script from devtools subspec to root + s.prepare_command = <<-CMD + ./xcodeinitscript.sh "#{layout_engine}" "#{js_engine}" + CMD + + s.subspec 'Framework' do |framework| + puts 'hippy subspec \'framework\' read begin' + framework.source_files = 'framework/ios/**/*.{h,m,c,mm,s,cpp,cc}' + framework.public_header_files = 'framework/ios/**/*.h' + if js_engine == "jsc" + framework.exclude_files = ['framework/ios/base/enginewrapper/v8', 'framework/ios/utils/v8'] + elsif js_engine == "v8" + framework.exclude_files = ['framework/ios/base/enginewrapper/jsc', 'framework/ios/utils/jsc'] + else + framework.exclude_files = ['framework/ios/base/enginewrapper/jsc', 'framework/ios/utils/jsc', 'framework/ios/base/enginewrapper/v8', 'framework/ios/utils/v8'] + end + framework.libraries = 'c++' + framework.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + framework.dependency 'hippy/Base' + framework.dependency 'hippy/JSDriver' + framework.dependency 'hippy/Image' + framework.dependency 'hippy/iOSVFS' + framework.dependency 'hippy/NativeRenderer' + puts 'hippy subspec \'framework\' read end' + end + + s.subspec 'Footstone' do |footstone| + puts 'hippy subspec \'footstone\' read begin' + footstone.libraries = 'c++' + footstone.source_files = ['modules/footstone/**/*.{h,cc}'] + footstone.public_header_files = ['modules/footstone/**/*.h'] + footstone.exclude_files = ['modules/footstone/include/footstone/platform/adr', 'modules/footstone/src/platform/adr'] + if use_frameworks + header_search_paths = "#{framework_header_path}" + " #{framework_header_path}/include" + footstone.header_mappings_dir = 'modules/footstone' + else + header_search_paths = '${PODS_ROOT}/hippy/modules/footstone/include' + + ' ${PODS_ROOT}/hippy/modules/footstone' + end + footstone.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'GCC_PREPROCESSOR_DEFINITIONS[config=Release]' => '${inherited} NDEBUG=1', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + 'HEADER_SEARCH_PATHS' => header_search_paths + } + footstone.user_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => header_search_paths + } + footstone.preserve_path = 'modules/footstone' + puts 'hippy subspec \'footstone\' read end' + end + + s.subspec 'FootstoneUtils' do |footstoneutils| + puts 'hippy subspec \'footstoneutils\' read begin' + footstoneutils.libraries = 'c++' + footstoneutils.source_files = ['modules/ios/footstoneutils/*.{h,mm}'] + footstoneutils.public_header_files = ['modules/ios/footstoneutils/*.h'] + footstoneutils.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + footstoneutils.dependency 'hippy/Footstone' + footstoneutils.dependency 'hippy/Base' + puts 'hippy subspec \'footstoneutils\' read end' + end + + s.subspec 'Image' do |image| + puts 'hippy subspec \'image\' read begin' + image.libraries = 'c++' + image.frameworks = 'CoreServices' + image.public_header_files = 'modules/ios/image/*.h' + image.source_files = 'modules/ios/image/*.{h,m,mm}' + puts 'hippy subspec \'image\' read end' + end + + s.subspec 'Base' do |base| + puts 'hippy subspec \'base\' read begin' + base.libraries = 'c++' + base.source_files = ['modules/ios/base/*.{h,m,mm}', 'modules/ios/logutils/*.{h,mm}'] + base.public_header_files = ['modules/ios/base/*.h', 'modules/ios/logutils/*.h'] + base.dependency 'hippy/Footstone' + puts 'hippy subspec \'base\' read end' + end + + s.subspec 'VFS' do |vfs| + puts 'hippy subspec \'vfs\' read begin' + vfs.libraries = 'c++' + vfs.source_files = ['modules/vfs/native/**/*.{h,cc}'] + vfs.public_header_files = ['modules/vfs/native/include/vfs/**/*.h'] + if use_frameworks + header_search_paths = framework_header_path + vfs.header_mappings_dir = 'modules/vfs/native/include' + else + header_search_paths = '${PODS_ROOT}/hippy/modules/vfs/native/include' + end + vfs.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'HEADER_SEARCH_PATHS' => header_search_paths, + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + vfs.user_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => header_search_paths + } + vfs.preserve_path = 'modules/vfs/native' + vfs.dependency 'hippy/Footstone' + puts 'hippy subspec \'vfs\' read end' + end + + s.subspec 'iOSVFS' do |iosvfs| + puts 'hippy subspec \'iosvfs\' read begin' + iosvfs.libraries = 'c++' + iosvfs.source_files = ['modules/vfs/ios/*.{h,m,mm}'] + iosvfs.public_header_files = ['modules/vfs/ios/*.h'] + iosvfs.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + iosvfs.dependency 'hippy/VFS' + iosvfs.dependency 'hippy/Footstone' + iosvfs.dependency 'hippy/FootstoneUtils' + puts 'hippy subspec \'iosvfs\' read end' + end + + s.subspec 'JSDriver' do |driver| + puts 'hippy subspec \'driver\' read begin' + driver.libraries = 'c++' + driver.frameworks = 'JavaScriptCore' + driver.source_files = ['driver/js/include/**/*.h', 'driver/js/src/**/*.cc'] + driver.public_header_files = 'driver/js/include/**/*.h' + if js_engine == "jsc" + driver.exclude_files = [ + 'driver/js/include/driver/napi/v8', + 'driver/js/src/napi/v8', + 'driver/js/include/driver/runtime', + 'driver/js/src/runtime', + 'driver/js/include/driver/vm/v8', + 'driver/js/src/vm/v8'] + elsif js_engine == "v8" + driver.exclude_files = [ + 'driver/js/include/driver/napi/jsc', + 'driver/js/src/napi/jsc', + 'driver/js/include/driver/vm/jsc', + 'driver/js/src/vm/jsc'] + else + driver.exclude_files = [ + 'driver/js/include/driver/napi/v8', + 'driver/js/src/napi/v8', + 'driver/js/include/driver/runtime', + 'driver/js/src/runtime', + 'driver/js/include/vm/v8', + 'driver/js/src/vm/v8', + 'driver/js/include/driver/napi/jsc', + 'driver/js/src/napi/jsc', + 'driver/js/include/vm/jsc', + 'driver/js/src/vm/jsc'] + end + + if use_frameworks + header_search_paths = framework_header_path + driver.header_mappings_dir = 'driver/js/include' + else + header_search_paths = '${PODS_ROOT}/hippy/driver/js/include/' + end + definition_engine = '' + if js_engine == "jsc" + definition_engine = 'JS_JSC=1' + elsif js_engine == "v8" + definition_engine = 'JS_V8=1' + else + end + driver.pod_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => header_search_paths, + 'GCC_PREPROCESSOR_DEFINITIONS' => definition_engine, + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + driver.user_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => header_search_paths, + } + driver.dependency 'hippy/Footstone' + driver.dependency 'hippy/Dom' + driver.dependency 'hippy/iOSVFS' + driver.preserve_path = 'driver/js' + puts 'hippy subspec \'driver\' read end' + end + + s.subspec 'NativeRenderer' do |renderer| + puts 'hippy subspec \'nativerenderer\' read begin' + renderer.libraries = 'c++' + renderer.source_files = 'renderer/native/ios/**/*.{h,m,mm}' + renderer.public_header_files = 'renderer/native/ios/**/*.h' + renderer.dependency 'hippy/Base' + renderer.dependency 'hippy/DomUtils' + renderer.dependency 'hippy/Image' + renderer.dependency 'hippy/iOSVFS' + puts 'hippy subspec \'nativerenderer\' read end' + end + + s.subspec 'Dom' do |dom| + puts 'hippy subspec \'dom\' read begin' + dom_source_files = Array['dom/include/**/*.h', 'dom/src/**/*.cc'] + dom_exclude_files = Array['dom/src/dom/*unittests.cc', + 'dom/src/dom/tools'] + if use_frameworks + dom_pod_target_header_path = framework_header_path + dom.header_mappings_dir = 'dom/include' + else + dom_pod_target_header_path = '${PODS_ROOT}/hippy/dom/include/' + end + if layout_engine == "Taitank" + dom_exclude_files.append('dom/include/dom/yoga_layout_node.h') + dom_exclude_files.append('dom/src/dom/yoga_layout_node.cc') + elsif layout_engine == "Yoga" + dom_exclude_files.append('dom/include/dom/taitank_layout_node.h') + dom_exclude_files.append('dom/src/dom/taitank_layout_node.cc') + end + + dom.libraries = 'c++' + dom.source_files = dom_source_files + dom.public_header_files = ['dom/include/**/*.h'] + dom.exclude_files = dom_exclude_files + dom.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'HEADER_SEARCH_PATHS' => dom_pod_target_header_path, + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + dom.user_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => dom_pod_target_header_path + } + dom.dependency 'hippy/Footstone' + if layout_engine == "Taitank" + dom.dependency 'hippy/Taitank' + elsif layout_engine == "Yoga" + dom.dependency 'hippy/Yoga' + end + dom.preserve_path = 'dom' + puts 'hippy subspec \'dom\' read end' + end + + s.subspec 'DomUtils' do |domutils| + puts 'hippy subspec \'domutils\' read begin' + dom_source_files = Array['modules/ios/domutils/*.{h,mm}'] + domutils.libraries = 'c++' + domutils.source_files = dom_source_files + domutils.public_header_files = ['modules/ios/domutils/*.h'] + domutils.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + domutils.dependency 'hippy/Dom' + domutils.dependency 'hippy/FootstoneUtils' + domutils.dependency 'hippy/Base' + puts 'hippy subspec \'domutils\' read end' + end + + if layout_engine == "Taitank" + s.subspec 'Taitank' do |taitank| + puts 'hippy subspec \'Taitank\' read begin' + taitank.source_files = ['dom/dom_project/_deps/taitank-src/src/*.{h,cc}'] + taitank.public_header_files = ['dom/include/dom/taitank_layout_node.h', 'dom/dom_project/_deps/taitank-src/src/*.h'] + if use_frameworks + header_search_paths = framework_header_path + else + header_search_paths = '${PODS_ROOT}/hippy/dom/dom_project/_deps/taitank-src/src' + end + taitank.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'HEADER_SEARCH_PATHS' => header_search_paths, + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + taitank.libraries = 'c++' + taitank.preserve_path = 'dom/dom_project' + puts 'hippy subspec \'Taitank\' read end' + end + elsif layout_engine == "Yoga" + s.subspec 'Yoga' do |yoga| + puts 'hippy subspec \'yoga\' read begin' + yoga.source_files = ['dom/dom_project/_deps/yoga-src/yoga/**/*.{c,h,cpp}'] + yoga.public_header_files = ['dom/include/dom/yoga_layout_node.h', 'dom/dom_project/_deps/yoga-src/yoga/**/*.h'] + if use_frameworks + header_search_paths = framework_header_path + else + header_search_paths = '${PODS_ROOT}/hippy/dom/dom_project/_deps/yoga-src' + end + yoga.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'HEADER_SEARCH_PATHS' => header_search_paths, + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + yoga.libraries = 'c++' + yoga.preserve_path = 'dom/dom_project' + puts 'hippy subspec \'yoga\' read end' + end + end + + #devtools subspec + s.subspec 'DevTools' do |devtools| + puts 'hippy subspec \'devtools\' read begin' + devtools.libraries = 'c++' + devtools_exclude_files = Array.new; + if js_engine == "jsc" + devtools_exclude_files += ['devtools/devtools-integration/native/include/devtools/v8', 'devtools/devtools-integration/native/src/v8'] + elsif js_engine == "v8" + else + devtools_exclude_files += ['devtools/devtools-integration/native/include/devtools/v8', 'devtools/devtools-integration/native/src/v8'] + end + devtools.exclude_files = [ + #test files + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/**/*test*/**/*', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/**/*test*', + #benchmark files + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/**/benchmark/**', + #js files + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/**/javascript/**', + #Dom includes all taitank or yoga files, and Devtools dependends on Dom, so let Dom does the including work, otherwise, 'duplicated symbols' error occurs + #taitank or yoga files + #currently Devtools specify taitank layout + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/taitank-*/**/*', + #other files + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/lib/lib_openmp.c', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/lib/tables/table_generator.c', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/lib/arch/**/{dec,enc}_*.c', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/bin/base64.c', + ] + devtools_exclude_files + devtools.public_header_files = [ + 'devtools/devtools-integration/native/include/devtools/devtools_data_source.h', + #devtools_integration/native + 'devtools/devtools-integration/native/**/*.h', + #devtools_backend + 'devtools/devtools-backend/**/*.{h,hpp}', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/*-src/**/*.{h,hpp,ipp}', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/lib/tables/*.h', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-build/config.h', + ] + devtools.source_files = [ + #devtools_integration/native + 'devtools/devtools-integration/native/**/*.{h,cc}', + #devtools_integration/ios + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/*-src/**/*.{h,hpp,c,cc,ipp}', + 'devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-build/config.h', + #devtools_backend + 'devtools/devtools-backend/**/*.{h,hpp,cc}', + ] + if use_frameworks + pod_search_path = "#{framework_header_path}/devtools-integration/ios/DevtoolsBackend/_deps/asio-src/asio/include" + + " #{framework_header_path}/devtools-integration/ios/DevtoolsBackend/_deps/json-src/include" + + " #{framework_header_path}/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/include" + + " #{framework_header_path}/devtools-integration/ios/DevtoolsBackend/_deps/websocketpp-src" + + " #{framework_header_path}/devtools-integration/native/include" + + " #{framework_header_path}/devtools-backend/include" + + " #{framework_header_path}/devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/lib/arch" + + devtools.header_mappings_dir = 'devtools' + else + pod_search_path = '${PODS_ROOT}/hippy/devtools/devtools-integration/ios/DevtoolsBackend/_deps/asio-src/asio/include' + + ' ${PODS_ROOT}/hippy/devtools/devtools-integration/ios/DevtoolsBackend/_deps/json-src/include' + + ' ${PODS_ROOT}/hippy/devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/include' + + ' ${PODS_ROOT}/hippy/devtools/devtools-integration/ios/DevtoolsBackend/_deps/websocketpp-src' + + ' ${PODS_ROOT}/hippy/devtools/devtools-integration/native/include' + + ' ${PODS_ROOT}/hippy/devtools/devtools-backend/include' + + ' ${PODS_ROOT}/hippy/devtools/devtools-integration/ios/DevtoolsBackend/_deps/base64-src/lib/arch' + end + devtools.pod_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => pod_search_path, + 'GCC_PREPROCESSOR_DEFINITIONS' => 'ENABLE_INSPECTOR=1 ASIO_NO_TYPEID ASIO_NO_EXCEPTIONS ASIO_DISABLE_ALIGNOF _WEBSOCKETPP_NO_EXCEPTIONS_ JSON_NOEXCEPTION BASE64_STATIC_DEFINE', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + devtools.user_target_xcconfig = { + 'GCC_PREPROCESSOR_DEFINITIONS' => 'ENABLE_INSPECTOR=1' + } + devtools.dependency 'hippy/Footstone' + devtools.dependency 'hippy/Dom' + devtools.dependency 'hippy/VFS' + devtools.preserve_path = 'devtools' + puts 'hippy subspec \'devtools\' read end' + end + + if js_engine == "v8" + s.subspec 'v8' do |v8| + puts 'hippy subspec \'v8\' read begin' + v8.source_files = ['v8forios/v8/include'] + v8.public_header_files = ['v8forios/v8/include'] + v8.pod_target_xcconfig = { + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'HEADER_SEARCH_PATHS' => '${PODS_ROOT}/hippy/v8forios/v8/include ${PODS_ROOT}/hippy/v8forios/v8/include/v8', + 'GCC_ENABLE_CPP_EXCEPTIONS' => false, + 'GCC_ENABLE_CPP_RTTI' => false, + } + v8.libraries = 'c++' + v8.vendored_library = 'v8forios/v8/libv8.a' + v8.preserve_path = 'v8forios/v8' + puts 'hippy subspec \'v8\' read end' + end + end + +end diff --git a/modules/ios/base/HippyAsserts.m b/modules/ios/base/HippyAsserts.m index a16a33ef4e4..04ec1686202 100644 --- a/modules/ios/base/HippyAsserts.m +++ b/modules/ios/base/HippyAsserts.m @@ -35,6 +35,15 @@ HippyFatalHandler HippyCurrentFatalHandler = nil; HippyExceptionHandler HippyCurrentExceptionHandler = nil; + +NSException *_HippyNotImplementedException(SEL, Class); +NSException *_HippyNotImplementedException(SEL cmd, Class cls) { + NSString *msg = [NSString stringWithFormat:@"%s is not implemented " + "for the class %@", + sel_getName(cmd), cls]; + return [NSException exceptionWithName:@"HippyNotDesignatedInitializerException" reason:msg userInfo:nil]; +} + /** * returns the topmost stacked assert function for the current thread, which * may not be the same as the current value of HippyCurrentAssertFunction. diff --git a/modules/ios/base/HippyDefines.h b/modules/ios/base/HippyDefines.h index 17bcccf9c5b..6eb974dfff3 100644 --- a/modules/ios/base/HippyDefines.h +++ b/modules/ios/base/HippyDefines.h @@ -66,3 +66,14 @@ #define kRootViewDidAddContent @"RootViewDidAddContent" #define kRootViewKey @"RootViewKey" + +/** + * Throw an assertion for unimplemented methods. + */ +#define HIPPY_NOT_IMPLEMENTED(method) \ +_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wmissing-method-return-type\"") \ +_Pragma("clang diagnostic ignored \"-Wunused-parameter\"") HIPPY_EXTERN NSException *_HippyNotImplementedException(SEL, Class); \ +method NS_UNAVAILABLE { \ +@throw _HippyNotImplementedException(_cmd, [self class]); \ +} \ +_Pragma("clang diagnostic pop") diff --git a/renderer/native/ios/renderer/HippyRootView.h b/renderer/native/ios/renderer/HippyRootView.h index 9aae5a31698..1c560fb123a 100644 --- a/renderer/native/ios/renderer/HippyRootView.h +++ b/renderer/native/ios/renderer/HippyRootView.h @@ -21,7 +21,33 @@ */ #import -#import "HippyInvalidating.h" +#import "HippyRootViewDelegate.h" + +@class HippyBridge; + +#pragma mark - + +/** + * This enum is used to define size flexibility type of the root view. + * If a dimension is flexible, the view will recalculate that dimension + * so the content fits. Recalculations are performed when the root's frame, + * size flexibility mode or content size changes. After a recalculation, + * rootViewDidChangeIntrinsicSize method of the HippyRootViewDelegate will be called. + */ +typedef NS_ENUM(NSInteger, HippyRootViewSizeFlexibility) { + HippyRootViewSizeFlexibilityNone = 0, + HippyRootViewSizeFlexibilityWidth, + HippyRootViewSizeFlexibilityHeight, + HippyRootViewSizeFlexibilityWidthAndHeight, +}; + +/** + * This notification is sent when the first subviews are added to the root view + * after the application has loaded. This is used to hide the `loadingView`, and + * is a good indicator that the application is ready to use. + */ +extern NSString *const HippyContentDidAppearNotification; + /** * Native view used to host Hippy-managed views within the app. Can be used just @@ -30,11 +56,84 @@ */ @interface HippyRootView : UIView +/// Create HippyRootView instance +/// +/// @param bridge the hippyBridge instance +/// @param moduleName module name +/// @param initialProperties application properties, see appProperties property. +/// @param delegate HippyRootViewDelegate +/// +- (instancetype)initWithBridge:(HippyBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + delegate:(id)delegate; + +/// Create HippyRootView instance +/// & Load the business BundleURL +/// & Run application +/// +/// @param bridge the hippyBridge instance +/// @param businessURL the bundleURL to load +/// @param moduleName module name +/// @param initialProperties application properties, see appProperties property. +/// @param delegate HippyRootViewDelegate +- (instancetype)initWithBridge:(HippyBridge *)bridge + businessURL:(NSURL *)businessURL + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + delegate:(id)delegate; + + +/** + * The name of the JavaScript module to execute within the + * specified scriptURL (required). Setting this will not have + * any immediate effect, but it must be done prior to loading + * the script. + */ +@property (nonatomic, copy, readonly) NSString *moduleName; + +/** + * The bridge used by the root view. Bridges can be shared between multiple + * root views, so you can use this property to initialize another HippyRootView. + */ +@property (nonatomic, strong, readonly) HippyBridge *bridge; + +/** + * The properties to apply to the view. Use this property to update + * application properties and rerender the view. Initialized with + * initialProperties argument of the initializer. + * + * Set this property only on the main thread. + */ +@property (nonatomic, copy, readwrite) NSDictionary *appProperties; + +/** + * The size flexibility mode of the root view. + */ +@property (nonatomic, assign) HippyRootViewSizeFlexibility sizeFlexibility; + +/** + * The size of the root view's content. This is set right before the + * rootViewDidChangeIntrinsicSize method of HippyRootViewDelegate is called. + */ +@property (readonly, nonatomic, assign) CGSize intrinsicSize; + +/** + * The delegate that handles intrinsic size updates. + */ +@property (nonatomic, weak) id delegate; + /** * The backing view controller of the root view. */ @property (nonatomic, weak) UIViewController *hippyViewController; +/** + * The Hippy-managed contents view of the root view. + */ +@property (nonatomic, strong, readonly) UIView *contentView; + + - (void)contentDidAppear; /** @@ -43,6 +142,21 @@ */ @property (nonatomic, assign) NSTimeInterval loadingViewFadeDelay; @property (nonatomic, assign) NSTimeInterval loadingViewFadeDuration; -@property (nonatomic, assign) Class customTouchHandler; + + + +- (void)runHippyApplication; + +#pragma mark - + +/// This method should be called when the host controller's view's size is changed +/// (i.e. for the root view controller when its window rotates or is resized). +/// +/// Note that `useViewWillTransitionMethodToMonitorOrientation` flag must be set when init bridge, +/// otherwise calling this function takes no effect. +/// +/// - Parameter size: the new size +- (void)onHostControllerTransitionedToSize:(CGSize)size; + @end diff --git a/renderer/native/ios/renderer/HippyRootView.m b/renderer/native/ios/renderer/HippyRootView.m deleted file mode 100644 index de7af80ef48..00000000000 --- a/renderer/native/ios/renderer/HippyRootView.m +++ /dev/null @@ -1,81 +0,0 @@ -/*! - * iOS SDK - * - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "HippyRootView.h" -#import "HippyAsserts.h" -#import "HippyView.h" -#import "UIView+Hippy.h" -#import "NativeRenderDefines.h" - -#include - -NSNumber *AllocRootViewTag(void) { - static NSString * const token = @"allocateRootTag"; - @synchronized (token) { - static NSUInteger rootTag = 0; - return @(rootTag += 10); - } -} - -@interface HippyRootView () { - BOOL _contentHasAppeared; -} - -@property (readwrite, nonatomic, assign) CGSize intrinsicSize; - -@end - -@implementation HippyRootView - -- (UIViewController *)hippyViewController { - return _hippyViewController?:[super hippyViewController]; -} - -- (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { - [super insertHippySubview:subview atIndex:atIndex]; - if (!_contentHasAppeared) { - _contentHasAppeared = YES; - [self contentDidAppear]; - [[NSNotificationCenter defaultCenter] postNotificationName:kRootViewDidAddContent object:nil userInfo:@{ - kRootViewKey: self - }]; - } -} - -- (NSNumber *)hippyTag { - HippyAssertMainQueue(); - if (!super.hippyTag) { - self.hippyTag = AllocRootViewTag(); - } - return super.hippyTag; -} - -- (void)contentDidAppear { -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - HippyLogInfo(@"[Hippy_OC_Log][Life_Circle],HippyRootView dealloc %p", self); -} - -@end diff --git a/renderer/native/ios/renderer/HippyRootView.mm b/renderer/native/ios/renderer/HippyRootView.mm new file mode 100644 index 00000000000..0f6b5a96c21 --- /dev/null +++ b/renderer/native/ios/renderer/HippyRootView.mm @@ -0,0 +1,399 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "HippyRootView.h" +#import "HippyAsserts.h" +#import "HippyView.h" +#import "UIView+Hippy.h" +#import "NativeRenderDefines.h" +#import "HippyInvalidating.h" +#import "HippyBridge.h" +#include + +NSString *const HippyContentDidAppearNotification = @"HippyContentDidAppearNotification"; + +NSNumber *AllocRootViewTag(void) { + static NSString * const token = @"allocateRootTag"; + @synchronized (token) { + static NSUInteger rootTag = 0; + return @(rootTag += 10); + } +} + + +@interface HippyRootContentView : HippyView + +@property (nonatomic, readonly) BOOL contentHasAppeared; +//@property (nonatomic, strong) HippyTouchHandler *touchHandler; +@property (nonatomic, assign) int64_t startTimpStamp; + +- (instancetype)initWithFrame:(CGRect)frame + bridge:(HippyBridge *)bridge + hippyTag:(NSNumber *)hippyTag + sizeFlexiblity:(HippyRootViewSizeFlexibility)sizeFlexibility NS_DESIGNATED_INITIALIZER; + +@end + + + +@interface HippyRootView () { + BOOL _contentHasAppeared; + HippyRootContentView *_contentView; +} + +/** + * The Hippy-managed contents view of the root view. + */ +@property (nonatomic, strong) UIView *contentView; + +@property (nonatomic, strong) NSDictionary *shareOptions; + +@end + +@implementation HippyRootView + +HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) +HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) + +- (instancetype)initWithBridge:(HippyBridge *)bridge + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + delegate:(id)delegate { + HippyAssertMainQueue(); + HippyAssert(bridge, @"A bridge instance is required to create an HippyRootView"); + HippyAssert(moduleName, @"A moduleName is required to create an HippyRootView"); + + if ((self = [super initWithFrame:CGRectZero])) { + self.backgroundColor = [UIColor clearColor]; + + _bridge = bridge; + if (nil == _bridge.moduleName) { + _bridge.moduleName = moduleName; + } + _moduleName = moduleName; + _appProperties = [initialProperties copy]; + _loadingViewFadeDelay = 0.25; + _loadingViewFadeDuration = 0.25; + _sizeFlexibility = HippyRootViewSizeFlexibilityNone; + _delegate = delegate; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + +// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bridgeDidReload) name:HippyJavaScriptWillStartLoadingNotification +// object:_bridge]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(javaScriptDidLoad:) name:HippyJavaScriptDidLoadNotification + object:_bridge]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(javaScriptDidFailToLoad:) + name:HippyJavaScriptDidFailToLoadNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_contentDidAppear:) name:HippyContentDidAppearNotification + object:self]; + +// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_secondaryBundleDidLoadSourceCode:) +// name:HippySecondaryBundleDidLoadSourceCodeNotification +// object:nil]; + +// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_secondayBundleDidFinishLoad:) +// name:HippySecondaryBundleDidLoadNotification +// object:nil]; + +// [self showLoadingView]; +// [_bridge.performanceLogger markStartForTag:HippyPLTTI]; + HippyLogInfo(@"[Hippy_OC_Log][Life_Circle],HippyRootView Init %p", self); + } + return self; +} + +- (instancetype)initWithBridge:(HippyBridge *)bridge + businessURL:(NSURL *)businessURL + moduleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initialProperties + delegate:(id)delegate { + if (self = [self initWithBridge:bridge + moduleName:moduleName + initialProperties:initialProperties + delegate:delegate]) { + if (!bridge.isValid) { + if (delegate && [delegate respondsToSelector:@selector(rootView:didLoadFinish:)]) { + [delegate rootView:self didLoadFinish:NO]; + } + } else { + __weak __typeof(self)weakSelf = self; + [bridge loadBundleURL:businessURL completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + if ([delegate respondsToSelector:@selector(rootView:didLoadFinish:)]) { + [delegate rootView:weakSelf didLoadFinish:(error == nil)]; + } + }); + if (!error) { + [weakSelf runHippyApplication]; + } + }]; + } + } + return self; +} + +- (void)dealloc { + [_contentView invalidate]; + if ([_delegate respondsToSelector:@selector(rootViewWillBePurged:)]) { + [_delegate rootViewWillBePurged:self]; + } + HippyLogInfo(@"[Hippy_OC_Log][Life_Circle],HippyRootView dealloc %p", self); +} + +- (void)runHippyApplication { + // [_bridge.performanceLogger markStartForTag:HippyPLRunApplication]; + + __weak __typeof(self)weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + [strongSelf.contentView removeFromSuperview]; + strongSelf.contentView = [[HippyRootContentView alloc] initWithFrame:strongSelf.bounds + bridge:strongSelf.bridge + hippyTag:strongSelf.hippyTag + sizeFlexiblity:strongSelf.sizeFlexibility]; + // 注册 + [strongSelf.bridge setRootView:strongSelf]; + [strongSelf.bridge loadInstanceForRootView:strongSelf.hippyTag withProperties:strongSelf.appProperties]; + HippyLogInfo(@"[Hippy_OC_Log][Life_Circle],Running application %@ (%@)", strongSelf.moduleName, strongSelf.appProperties); + }); +} + + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + super.backgroundColor = backgroundColor; + _contentView.backgroundColor = backgroundColor; +} + +- (UIViewController *)hippyViewController { + return _hippyViewController?:[super hippyViewController]; +} + +- (BOOL)canBecomeFirstResponder { + return YES; +} + +- (void)cancelTouches { +// [[_contentView touchHandler] cancelTouch]; +} + +- (NSNumber *)hippyTag { + HippyAssertMainQueue(); + if (!super.hippyTag) { + self.hippyTag = AllocRootViewTag(); + } + return super.hippyTag; +} + +- (void)bridgeDidReload { + HippyAssertMainQueue(); + // Clear the hippyTag so it can be re-assigned + self.hippyTag = nil; +} + +//- (void)javaScriptDidLoad:(NSNotification *)notification { +// HippyAssertMainQueue(); +// +// // Use the (batched) bridge that's sent in the notification payload, so the +// // HippyRootContentView is scoped to the right bridge +// HippyBridge *bridge = notification.userInfo[@"bridge"]; +// if (!bridge.useCommonBridge && _bridge.batchedBridge == bridge) { +// [self bundleFinishedLoading:bridge]; +// } +//} +// +//- (void)javaScriptDidFailToLoad:(NSNotification *)notification { +// HippyBridge *bridge = notification.userInfo[@"bridge"]; +// NSError *error = notification.userInfo[@"error"]; +// if (bridge == self.bridge && error) { +// NSError *retError = HippyErrorFromErrorAndModuleName(error, self.bridge.moduleName); +// HippyFatal(retError); +// } +//} + +- (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { + [super insertHippySubview:subview atIndex:atIndex]; + if (!_contentHasAppeared) { + _contentHasAppeared = YES; + [self contentDidAppear]; + [[NSNotificationCenter defaultCenter] postNotificationName:kRootViewDidAddContent object:nil userInfo:@{ + kRootViewKey: self + }]; + } +} + +- (void)contentDidAppear { +} + + + + +- (void)setSizeFlexibility:(HippyRootViewSizeFlexibility)sizeFlexibility { + _sizeFlexibility = sizeFlexibility; + [self setNeedsLayout]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + _contentView.frame = self.bounds; +// _loadingView.center = (CGPoint) { CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds) }; +} + +- (void)setAppProperties:(NSDictionary *)appProperties { + HippyAssertMainQueue(); + + if ([_appProperties isEqualToDictionary:appProperties]) { + return; + } + + _appProperties = [appProperties copy]; + + if (_contentView && _bridge.valid && !_bridge.loading) { + [self runHippyApplication]; + } +} + +- (void)setIntrinsicSize:(CGSize)intrinsicSize { + BOOL oldSizeHasAZeroDimension = _intrinsicSize.height == 0 || _intrinsicSize.width == 0; + BOOL newSizeHasAZeroDimension = intrinsicSize.height == 0 || intrinsicSize.width == 0; + BOOL bothSizesHaveAZeroDimension = oldSizeHasAZeroDimension && newSizeHasAZeroDimension; + + BOOL sizesAreEqual = CGSizeEqualToSize(_intrinsicSize, intrinsicSize); + + _intrinsicSize = intrinsicSize; + + // Don't notify the delegate if the content remains invisible or its size has not changed + if (bothSizesHaveAZeroDimension || sizesAreEqual) { + return; + } + if ([_delegate respondsToSelector:@selector(rootViewDidChangeIntrinsicSize:)]) { + [_delegate rootViewDidChangeIntrinsicSize:self]; + } +} + +- (void)contentViewInvalidated { + [_contentView removeFromSuperview]; + _contentView = nil; +// [self showLoadingView]; +} + + +#pragma mark - + +//- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { +// [super traitCollectionDidChange:previousTraitCollection]; +// if (@available(iOS 12.0, *)) { +// // on dark mode change +// UIUserInterfaceStyle currentStyle = self.traitCollection.userInterfaceStyle; +// if (currentStyle != previousTraitCollection.userInterfaceStyle) { +// BOOL isNightMode = (UIUserInterfaceStyleDark == currentStyle); +// if (self.bridge.isOSNightMode != isNightMode) { +// [self.bridge setOSNightMode:isNightMode withRootViewTag:self.hippyTag]; +// } +// } +// } +//} +// +//static NSString *const HippyHostControllerSizeKeyNewSize = @"NewSize"; +//- (void)onHostControllerTransitionedToSize:(CGSize)size { +// [NSNotificationCenter.defaultCenter postNotificationName:HippyDimensionsShouldUpdateNotification +// object:nil +// userInfo:@{HippyHostControllerSizeKeyNewSize : @(size)}]; +//} + +@end + + +@implementation HippyRootContentView { + __weak HippyBridge *_bridge; + UIColor *_backgroundColor; +} + +- (instancetype)initWithFrame:(CGRect)frame + bridge:(HippyBridge *)bridge + hippyTag:(NSNumber *)hippyTag + sizeFlexiblity:(HippyRootViewSizeFlexibility)sizeFlexibility { + if ((self = [super initWithFrame:frame])) { + _bridge = bridge; + self.hippyTag = hippyTag; + +// _touchHandler = [[HippyTouchHandler alloc] initWithRootView:self bridge:bridge]; +// [self addGestureRecognizer:_touchHandler]; +// [_bridge.uiManager registerRootView:self withSizeFlexibility:sizeFlexibility]; + + self.layer.backgroundColor = NULL; + _startTimpStamp = CACurrentMediaTime() * 1000; + } + return self; +} + +HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) +HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (nonnull NSCoder *)aDecoder) + +- (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { + [super insertHippySubview:subview atIndex:atIndex]; +// [_bridge.performanceLogger markStopForTag:HippyPLTTI]; + + dispatch_async(dispatch_get_main_queue(), ^{ + if (!self->_contentHasAppeared) { + self->_contentHasAppeared = YES; +// int64_t cost = [self->_bridge.performanceLogger durationForTag:HippyPLTTI]; +// [[NSNotificationCenter defaultCenter] postNotificationName:HippyContentDidAppearNotification object:self.superview userInfo:@{ +// @"cost": @(cost) +// }]; + } + }); +} + +- (void)setFrame:(CGRect)frame { + CGRect originFrame = self.frame; + if (!CGRectEqualToRect(originFrame, frame)) { + super.frame = frame; + if (self.hippyTag && _bridge.isValid) { +// [_bridge.uiManager setFrame:frame fromOriginFrame:originFrame forView:self]; + } + } +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + _backgroundColor = backgroundColor; + if (self.hippyTag && _bridge.isValid) { +// [_bridge.uiManager setBackgroundColor:backgroundColor forView:self]; + } +} + +- (UIColor *)backgroundColor { + return _backgroundColor; +} + +- (void)invalidate { + if (self.userInteractionEnabled) { + self.userInteractionEnabled = NO; + [(HippyRootView *)self.superview contentViewInvalidated]; + [_bridge enqueueJSCall:@"AppRegistry" method:@"unmountApplicationComponentAtRootTag" args:@[self.hippyTag] completion:NULL]; + } +} + +@end diff --git a/renderer/native/ios/renderer/HippyRootViewDelegate.h b/renderer/native/ios/renderer/HippyRootViewDelegate.h new file mode 100644 index 00000000000..f9716866720 --- /dev/null +++ b/renderer/native/ios/renderer/HippyRootViewDelegate.h @@ -0,0 +1,50 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class HippyRootView; + +@protocol HippyRootViewDelegate + +@optional +/** + * Called after the root view's content is updated to a new size. The method is not called + * when both old size and new size have a dimension that equals to zero. + * + * The delegate can use this callback to appropriately resize the root view frame to fit the new + * content view size. The view will not resize itself. The new content size is available via the + * intrinsicSize propery of the root view. + */ +- (void)rootViewDidChangeIntrinsicSize:(HippyRootView *)rootView; + +/** + * Called after finish load the bundle. + */ +- (void)rootView:(HippyRootView *)rootView didLoadFinish:(BOOL)success; + +/** + * Called when rootview dealloc. + */ +- (void)rootViewWillBePurged:(HippyRootView *)rootView; + +@end