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 convenience init() { 23 self.init(appContext: AppContext()) 24 } 25 26 public init(appContext: AppContext) { 27 self.appContext = appContext 28 super.init() 29 } 30 31 deinit { 32 NotificationCenter.default.removeObserver(self) 33 } 34 35 // MARK: - RCTBridgeModule 36 37 public static func moduleName() -> String! { 38 return "Expo" 39 } 40 41 public static func requiresMainQueueSetup() -> Bool { 42 return true 43 } 44 45 public var bridge: RCTBridge! { 46 didSet { 47 appContext.reactBridge = bridge 48 49 let attachRuntime = { [weak self] in 50 guard let self = self, let bridge = self.appContext.reactBridge else { 51 return 52 } 53 self.appContext._runtime = EXJavaScriptRuntimeManager.runtime(fromBridge: bridge) 54 } 55 56 if bridge.responds(to: Selector(("runtime"))) { 57 // Getting the `runtime` on a different thread than JS is considered to be dangerous. 58 // However, we just checking if it exists. We don't do anything with it. 59 let result = bridge.perform(Selector(("runtime"))) 60 if result == nil { 61 // When exporting expo modules using `extraModulesForBridge` (e.g. in Expo Go), 62 // the runtime won't be initiated before the bridge didSet method is called. 63 // Therefore, we need to wait for the main thread to complete its initialization by dispatching on it first. 64 bridge.dispatchBlock({ [weak self] in 65 guard let self = self, let bridge = self.appContext.reactBridge else { 66 return 67 } 68 bridge.dispatchBlock(attachRuntime, queue: RCTJSThread) 69 }, queue: DispatchQueue.main) 70 return 71 } 72 } 73 74 bridge.dispatchBlock(attachRuntime, queue: RCTJSThread) 75 } 76 } 77 78 /** 79 This should be called inside EXNativeModulesProxy.setBridge() 80 */ 81 @objc 82 public func legacyProxyDidSetBridge(legacyModulesProxy: LegacyNativeModulesProxy, 83 legacyModuleRegistry: EXModuleRegistry) { 84 appContext.legacyModuleRegistry = legacyModuleRegistry 85 appContext.legacyModulesProxy = legacyModulesProxy 86 87 // we need to register all the modules after the legacy module registry is set 88 // otherwise legacy modules (e.g. permissions) won't be available in OnCreate { } 89 appContext.useModulesProvider("ExpoModulesProvider") 90 appContext.moduleRegistry.register(moduleType: NativeModulesProxyModule.self) 91 } 92 } 93