1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 import React 4 import Foundation 5 6 /** 7 The classic bridge module that is responsible for: 8 - Creating and owning the `AppContext` when the Expo modules architecture is automatically initialized 9 by React Native (as opposed to native unit tests, where React Native is not used at all). 10 - Installing the host object to the runtime. 11 */ 12 @objc(ExpoBridgeModule) 13 public final class ExpoBridgeModule: NSObject, RCTBridgeModule { 14 @objc 15 public let appContext: AppContext 16 17 /** 18 The initializer that is used by React Native when it loads bridge modules. 19 In this scenario, we create an `AppContext` that manages the 20 architecture of Expo modules and the app itself. 21 */ 22 override init() { 23 appContext = AppContext() 24 super.init() 25 } 26 27 deinit { 28 NotificationCenter.default.removeObserver(self) 29 } 30 31 // MARK: - RCTBridgeModule 32 33 public static func moduleName() -> String! { 34 return "Expo" 35 } 36 37 public static func requiresMainQueueSetup() -> Bool { 38 return true 39 } 40 41 public var bridge: RCTBridge! { 42 didSet { 43 appContext.reactBridge = bridge 44 45 let attachRuntime = { [weak self] in 46 guard let self = self, let bridge = self.appContext.reactBridge else { 47 return 48 } 49 self.appContext._runtime = EXJavaScriptRuntimeManager.runtime(fromBridge: bridge) 50 } 51 52 if bridge.responds(to: Selector(("runtime"))) { 53 // Getting the `runtime` on a different thread than JS is considered to be dangerous. 54 // However, we just checking if it exists. We don't do anything with it. 55 let result = bridge.perform(Selector(("runtime"))) 56 if result == nil { 57 // When exporting expo modules using `extraModulesForBridge` (e.g. in Expo Go), 58 // the runtime won't be initiated before the bridge didSet method is called. 59 // Therefore, we need to wait for the main thread to complete its initialization by dispatching on it first. 60 bridge.dispatchBlock({ [weak self] in 61 guard let self = self, let bridge = self.appContext.reactBridge else { 62 return 63 } 64 bridge.dispatchBlock(attachRuntime, queue: RCTJSThread) 65 }, queue: DispatchQueue.main) 66 return 67 } 68 } 69 70 bridge.dispatchBlock(attachRuntime, queue: RCTJSThread) 71 } 72 } 73 74 /** 75 This should be called inside EXNativeModulesProxy.setBridge() 76 */ 77 @objc 78 public func legacyProxyDidSetBridge(legacyModulesProxy: LegacyNativeModulesProxy, 79 legacyModuleRegistry: EXModuleRegistry) { 80 appContext.legacyModuleRegistry = legacyModuleRegistry 81 appContext.legacyModulesProxy = legacyModulesProxy 82 83 // we need to register all the modules after the legacy module registry is set 84 // otherwise legacy modules (e.g. permissions) won't be available in OnCreate { } 85 appContext.useModulesProvider("ExpoModulesProvider") 86 appContext.moduleRegistry.register(moduleType: NativeModulesProxyModule.self) 87 } 88 } 89