1 // Copyright 2023-present 650 Industries. All rights reserved. 2 3 import React 4 import ExpoModulesCore 5 import EXManifests 6 7 /** 8 Manages the React Native app opened in Expo Go. As opposed to ``EXReactAppManager`` and ``EXHomeAppManager``, 9 this manager is versioned and used by them under the hood like an adapter. 10 */ 11 @objc(EXVersionManager) 12 final class VersionManager: EXVersionManagerObjC { 13 var appContext: AppContext? 14 var legacyModulesProxy: LegacyNativeModulesProxy? 15 var legacyModuleRegistry: EXModuleRegistry? 16 17 let params: [AnyHashable: Any] 18 19 let manifest: Manifest 20 21 @objc 22 override init( 23 params: [AnyHashable: Any], 24 manifest: Manifest, 25 fatalHandler: @escaping (Error?) -> Void, 26 logFunction: @escaping RCTLogFunction, 27 logThreshold: RCTLogLevel 28 ) { 29 self.params = params 30 self.manifest = manifest 31 32 configureReact( 33 enableTurboModules: manifest.experiments()?["turboModules"] as? Bool ?? false, 34 fatalHandler: fatalHandler, 35 logFunction: logFunction, 36 logThreshold: logThreshold 37 ) 38 super.init(params: params, manifest: manifest, fatalHandler: fatalHandler, logFunction: logFunction, logThreshold: logThreshold) 39 } 40 41 /** 42 Invalidates the app context when the bridge is about to be rebuilt. 43 */ 44 override func invalidate() { 45 appContext = nil 46 super.invalidate() 47 } 48 49 /** 50 Returns a list of bridge modules to register when the bridge initializes. 51 */ 52 @objc 53 override func extraModules(forBridge bridge: Any) -> [Any] { 54 // Ideally if we don't initialize the app context here, but unfortunately there is no better place in bridge lifecycle 55 // that would work well for us (especially properly invalidating existing app context on reload). 56 let legacyModuleRegistry = createLegacyModuleRegistry(params: params, manifest: manifest) 57 let legacyModulesProxy = LegacyNativeModulesProxy(customModuleRegistry: legacyModuleRegistry) 58 let appContext = AppContext(legacyModulesProxy: legacyModulesProxy, legacyModuleRegistry: legacyModuleRegistry) 59 60 self.appContext = appContext 61 self.legacyModuleRegistry = legacyModuleRegistry 62 self.legacyModulesProxy = legacyModulesProxy 63 64 let modules: [Any] = [ 65 EXAppState(), 66 EXDisabledDevLoadingView(), 67 EXStatusBarManager(), 68 69 // Adding EXNativeModulesProxy with the custom moduleRegistry. 70 legacyModulesProxy, 71 72 // Adding the way to access the module registry from RCTBridgeModules. 73 EXModuleRegistryHolderReactModule(moduleRegistry: legacyModuleRegistry), 74 75 // When ExpoBridgeModule is initialized by RN, it automatically creates the app context. 76 // In Expo Go, it has to use already created app context. 77 ExpoBridgeModule(appContext: appContext) 78 ] 79 80 // Register additional Expo modules, specific to Expo Go. 81 registerExpoModules() 82 83 return modules + super.extraModules(forBridge: bridge) 84 } 85 86 /** 87 Registers Expo modules that are not generated in ``ExpoModulesProvider``, but are necessary for Expo Go apps. 88 */ 89 private func registerExpoModules() { 90 guard let appContext else { 91 log.error("Unable to register Expo modules, the app context is unavailable") 92 return 93 } 94 appContext.moduleRegistry.register(module: ExpoGoModule(appContext: appContext, manifest: manifest)) 95 } 96 } 97 98 /** 99 Configures some React Native global options. 100 */ 101 private func configureReact( 102 enableTurboModules: Bool, 103 fatalHandler: @escaping (Error?) -> Void, 104 logFunction: @escaping RCTLogFunction, 105 logThreshold: RCTLogLevel 106 ) { 107 RCTEnableTurboModule(enableTurboModules) 108 RCTSetFatalHandler(fatalHandler) 109 RCTSetLogThreshold(logThreshold) 110 RCTSetLogFunction(logFunction) 111 } 112 113 /** 114 Creates a module registry for legacy Expo modules. 115 */ 116 private func createLegacyModuleRegistry(params: [AnyHashable: Any], manifest: Manifest) -> EXModuleRegistry { 117 guard let singletonModules = params["singletonModules"] as? Set<AnyHashable> else { 118 fatalError("Singleton modules param cannot be cast to Set<AnyHashable>") 119 } 120 let moduleRegistryProvider = ModuleRegistryProvider(singletonModules: singletonModules) 121 let moduleRegistryAdapter = EXScopedModuleRegistryAdapter(moduleRegistryProvider: moduleRegistryProvider) 122 123 moduleRegistryProvider.moduleRegistryDelegate = EXScopedModuleRegistryDelegate(params: params) 124 125 return moduleRegistryAdapter.moduleRegistry( 126 forParams: params, 127 forExperienceStableLegacyId: manifest.stableLegacyId(), 128 scopeKey: manifest.scopeKey(), 129 manifest: manifest, 130 withKernelServices: params["services"] as? [AnyHashable: Any] 131 ) 132 } 133