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