1 // Copyright 2015-present 650 Industries. All rights reserved. 2 import SafariServices 3 4 @objc(DevMenuInternalModule) 5 public class DevMenuInternalModule: NSObject, RCTBridgeModule { 6 public static func moduleName() -> String! { 7 return "ExpoDevMenuInternal" 8 } 9 10 // Module DevMenuInternalModule requires main queue setup since it overrides `constantsToExport`. 11 public static func requiresMainQueueSetup() -> Bool { 12 return true 13 } 14 15 let manager: DevMenuManager 16 17 public override init() { 18 self.manager = DevMenuManager.shared 19 } 20 21 init(manager: DevMenuManager) { 22 self.manager = manager 23 } 24 25 // MARK: JavaScript API 26 27 @objc 28 public func constantsToExport() -> [AnyHashable: Any] { 29 #if targetEnvironment(simulator) 30 let doesDeviceSupportKeyCommands = true 31 #else 32 let doesDeviceSupportKeyCommands = false 33 #endif 34 return ["doesDeviceSupportKeyCommands": doesDeviceSupportKeyCommands] 35 } 36 37 @objc 38 func fetchDataSourceAsync(_ dataSourceId: String?, resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 39 guard let dataSourceId = dataSourceId else { 40 return reject("ERR_DEVMENU_DATA_SOURCE_FAILED", "DataSource ID not provided.", nil) 41 } 42 43 for dataSource in manager.devMenuDataSources { 44 if dataSource.id == dataSourceId { 45 dataSource.fetchData { data in 46 resolve(data.map { $0.serialize() }) 47 } 48 return 49 } 50 } 51 52 return reject("ERR_DEVMENU_DATA_SOURCE_FAILED", "DataSource \(dataSourceId) not founded.", nil) 53 } 54 55 @objc 56 func dispatchCallableAsync(_ callableId: String?, args: [String: Any]?, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 57 guard let callableId = callableId else { 58 return reject("ERR_DEVMENU_ACTION_FAILED", "Callable ID not provided.", nil) 59 } 60 manager.dispatchCallable(withId: callableId, args: args) 61 resolve(nil) 62 } 63 64 @objc 65 func hideMenu() { 66 manager.hideMenu() 67 } 68 69 @objc 70 func setOnboardingFinished(_ finished: Bool) { 71 DevMenuPreferences.isOnboardingFinished = finished 72 } 73 74 @objc 75 func openDevMenuFromReactNative() { 76 guard let rctDevMenu = manager.currentBridge?.devMenu else { 77 return 78 } 79 80 DispatchQueue.main.async { 81 rctDevMenu.show() 82 } 83 } 84 85 @objc 86 func onScreenChangeAsync(_ currentScreen: String?, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 87 manager.setCurrentScreen(currentScreen) 88 resolve(nil) 89 } 90 } 91