1 // Copyright 2015-present 650 Industries. All rights reserved.
2 
3 import React
4 
5 @objc
6 class DevMenuAppInstance: DevMenuRCTCxxBridgeDelegate, RCTBridgeDelegate {
7   static private var CloseEventName = "closeDevMenu"
8   static private var OpenEventName = "openDevMenu"
9 
10   private let manager: DevMenuManager
11 
12   var bridge: RCTBridge?
13 
14   init(manager: DevMenuManager) {
15     self.manager = manager
16 
17     super.init()
18 
19     self.bridge = DevMenuRCTBridge.init(delegate: self, launchOptions: nil)
20   }
21 
22   init(manager: DevMenuManager, bridge: RCTBridge) {
23     self.manager = manager
24 
25     super.init()
26 
27     self.bridge = bridge
28   }
29 
30   /**
31    Sends an event to JS triggering the animation that collapses the dev menu.
32    */
33   public func sendCloseEvent() {
34     bridge?.enqueueJSCall("RCTDeviceEventEmitter.emit", args: [DevMenuAppInstance.CloseEventName])
35   }
36 
37   public func sendOpenEvent() {
38     bridge?.enqueueJSCall("RCTDeviceEventEmitter.emit", args: [DevMenuAppInstance.OpenEventName])
39   }
40 
41   // MARK: RCTBridgeDelegate
42 
43   func sourceURL(for bridge: RCTBridge!) -> URL! {
44     #if DEBUG
45     if let packagerHost = jsPackagerHost() {
46       return RCTBundleURLProvider.jsBundleURL(
47         forBundleRoot: "index",
48         packagerHost: packagerHost,
49         enableDev: true,
50         enableMinification: false)
51     }
52     #endif
53     return jsSourceUrl()
54   }
55 
56   func extraModules(for bridge: RCTBridge!) -> [RCTBridgeModule]! {
57     var modules: [RCTBridgeModule] = [DevMenuLoadingView.init()]
58     modules.append(DevMenuRCTDevSettings.init())
59     return modules
60   }
61 
62   func bridge(_ bridge: RCTBridge!, didNotFindModule moduleName: String!) -> Bool {
63     return moduleName == "DevMenu"
64   }
65 
66   // MARK: private
67 
68   private func jsSourceUrl() -> URL? {
69     return DevMenuUtils.resourcesBundle()?.url(forResource: "EXDevMenuApp.ios", withExtension: "js")
70   }
71 
72   private func jsPackagerHost() -> String? {
73     // Return `nil` if resource doesn't exist in the bundle.
74     guard let packagerHostPath = DevMenuUtils.resourcesBundle()?.path(forResource: "dev-menu-packager-host", ofType: nil) else {
75       return nil
76     }
77     // Return `nil` if the content is not a valid URL.
78     guard let content = try? String(contentsOfFile: packagerHostPath, encoding: String.Encoding.utf8).trimmingCharacters(in: CharacterSet.newlines),
79       let url = URL(string: content) else {
80       return nil
81     }
82     return url.absoluteString
83   }
84 }
85