@capacitor-plus/ios 8.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/Capacitor/Capacitor/AppUUID.swift +51 -0
  2. package/Capacitor/Capacitor/Array+Capacitor.swift +31 -0
  3. package/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift +32 -0
  4. package/Capacitor/Capacitor/CAPBridge.swift +25 -0
  5. package/Capacitor/Capacitor/CAPBridgeDelegate.swift +6 -0
  6. package/Capacitor/Capacitor/CAPBridgeProtocol.swift +148 -0
  7. package/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h +6 -0
  8. package/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m +5 -0
  9. package/Capacitor/Capacitor/CAPBridgeViewController.swift +384 -0
  10. package/Capacitor/Capacitor/CAPBridgedJSTypes.h +18 -0
  11. package/Capacitor/Capacitor/CAPBridgedJSTypes.m +43 -0
  12. package/Capacitor/Capacitor/CAPBridgedPlugin+getMethod.swift +13 -0
  13. package/Capacitor/Capacitor/CAPBridgedPlugin.h +41 -0
  14. package/Capacitor/Capacitor/CAPFile.swift +14 -0
  15. package/Capacitor/Capacitor/CAPInstanceConfiguration.h +38 -0
  16. package/Capacitor/Capacitor/CAPInstanceConfiguration.m +93 -0
  17. package/Capacitor/Capacitor/CAPInstanceConfiguration.swift +78 -0
  18. package/Capacitor/Capacitor/CAPInstanceDescriptor.h +167 -0
  19. package/Capacitor/Capacitor/CAPInstanceDescriptor.m +56 -0
  20. package/Capacitor/Capacitor/CAPInstanceDescriptor.swift +198 -0
  21. package/Capacitor/Capacitor/CAPInstancePlugin.swift +10 -0
  22. package/Capacitor/Capacitor/CAPLog.swift +11 -0
  23. package/Capacitor/Capacitor/CAPNotifications.swift +69 -0
  24. package/Capacitor/Capacitor/CAPPlugin+LoadInstance.swift +20 -0
  25. package/Capacitor/Capacitor/CAPPlugin.h +60 -0
  26. package/Capacitor/Capacitor/CAPPlugin.m +180 -0
  27. package/Capacitor/Capacitor/CAPPluginCall.h +25 -0
  28. package/Capacitor/Capacitor/CAPPluginCall.m +36 -0
  29. package/Capacitor/Capacitor/CAPPluginCall.swift +97 -0
  30. package/Capacitor/Capacitor/CAPPluginMethod.h +36 -0
  31. package/Capacitor/Capacitor/CAPPluginMethod.m +44 -0
  32. package/Capacitor/Capacitor/CAPPluginMethod.swift +17 -0
  33. package/Capacitor/Capacitor/Capacitor.h +15 -0
  34. package/Capacitor/Capacitor/Capacitor.modulemap +8 -0
  35. package/Capacitor/Capacitor/CapacitorBridge.swift +768 -0
  36. package/Capacitor/Capacitor/CapacitorExtension.swift +24 -0
  37. package/Capacitor/Capacitor/Codable/JSValueDecoder.swift +479 -0
  38. package/Capacitor/Capacitor/Codable/JSValueEncoder.swift +671 -0
  39. package/Capacitor/Capacitor/Data+Capacitor.swift +22 -0
  40. package/Capacitor/Capacitor/DocLinks.swift +8 -0
  41. package/Capacitor/Capacitor/Info.plist +24 -0
  42. package/Capacitor/Capacitor/JS.swift +122 -0
  43. package/Capacitor/Capacitor/JSExport.swift +218 -0
  44. package/Capacitor/Capacitor/JSTypes.swift +254 -0
  45. package/Capacitor/Capacitor/KeyPath.swift +60 -0
  46. package/Capacitor/Capacitor/KeyValueStore.swift +303 -0
  47. package/Capacitor/Capacitor/NotificationHandlerProtocol.swift +6 -0
  48. package/Capacitor/Capacitor/NotificationRouter.swift +60 -0
  49. package/Capacitor/Capacitor/PluginCallResult.swift +106 -0
  50. package/Capacitor/Capacitor/PluginConfig.swift +56 -0
  51. package/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift +141 -0
  52. package/Capacitor/Capacitor/Plugins/CapacitorCookies.swift +57 -0
  53. package/Capacitor/Capacitor/Plugins/CapacitorHttp.swift +57 -0
  54. package/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift +225 -0
  55. package/Capacitor/Capacitor/Plugins/Console.swift +16 -0
  56. package/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift +236 -0
  57. package/Capacitor/Capacitor/Plugins/SystemBars.swift +133 -0
  58. package/Capacitor/Capacitor/Plugins/WebView.swift +44 -0
  59. package/Capacitor/Capacitor/PrivacyInfo.xcprivacy +14 -0
  60. package/Capacitor/Capacitor/Router.swift +29 -0
  61. package/Capacitor/Capacitor/TmpViewController.swift +8 -0
  62. package/Capacitor/Capacitor/UIColor.swift +55 -0
  63. package/Capacitor/Capacitor/UIStatusBarManager+CAPHandleTapAction.m +37 -0
  64. package/Capacitor/Capacitor/WKWebView+Capacitor.m +15 -0
  65. package/Capacitor/Capacitor/WKWebView+Capacitor.swift +80 -0
  66. package/Capacitor/Capacitor/WebViewAssetHandler.swift +550 -0
  67. package/Capacitor/Capacitor/WebViewDelegationHandler.swift +361 -0
  68. package/Capacitor/Capacitor/assets/native-bridge.js +1029 -0
  69. package/Capacitor.podspec +23 -0
  70. package/CapacitorCordova/CapacitorCordova/CapacitorCordova.h +25 -0
  71. package/CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap +6 -0
  72. package/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.h +8 -0
  73. package/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.m +5 -0
  74. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h +28 -0
  75. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h +109 -0
  76. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailabilityDeprecated.h +21 -0
  77. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h +49 -0
  78. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h +39 -0
  79. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m +149 -0
  80. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.h +31 -0
  81. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.m +81 -0
  82. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h +52 -0
  83. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m +116 -0
  84. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.h +39 -0
  85. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.m +38 -0
  86. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h +76 -0
  87. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m +136 -0
  88. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.h +25 -0
  89. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.m +77 -0
  90. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h +82 -0
  91. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m +216 -0
  92. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVScreenOrientationDelegate.h +33 -0
  93. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.h +27 -0
  94. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.m +74 -0
  95. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.h +30 -0
  96. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.m +37 -0
  97. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.h +34 -0
  98. package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.m +55 -0
  99. package/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.h +35 -0
  100. package/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.m +63 -0
  101. package/CapacitorCordova/CapacitorCordova/Info.plist +26 -0
  102. package/CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy +14 -0
  103. package/CapacitorCordova.podspec +26 -0
  104. package/LICENSE +21 -0
  105. package/package.json +33 -0
  106. package/scripts/pods_helpers.rb +12 -0
@@ -0,0 +1,768 @@
1
+ import Foundation
2
+ import Dispatch
3
+ import WebKit
4
+ import Cordova
5
+
6
+ internal typealias CapacitorPlugin = CAPPlugin & CAPBridgedPlugin
7
+
8
+ struct RegistrationList: Codable {
9
+ let packageClassList: Set<String>
10
+ }
11
+
12
+ /**
13
+ An internal class adopting a public protocol means that we have a lot of `public` methods
14
+ but that is by design not a mistake. And since the bridge is the center of the whole project
15
+ its size/complexity is unavoidable.
16
+
17
+ Quiet these warnings for the whole file.
18
+ */
19
+ // swiftlint:disable lower_acl_than_parent
20
+ // swiftlint:disable file_length
21
+ // swiftlint:disable type_body_length
22
+ open class CapacitorBridge: NSObject, CAPBridgeProtocol {
23
+
24
+ // this decision is needed before the bridge is instantiated,
25
+ // so we need a class property to avoid duplication
26
+ public static var isDevEnvironment: Bool {
27
+ #if DEBUG
28
+ return true
29
+ #else
30
+ // this is needed for SPM xcframework Capacitor. Can eventually be removed when the SPM package moves to being source-based.
31
+ if let debugValue = Bundle.main.object(forInfoDictionaryKey: "CAPACITOR_DEBUG") as? String, debugValue == "true" {
32
+ return true
33
+ }
34
+ return false
35
+ #endif
36
+ }
37
+
38
+ // MARK: - CAPBridgeProtocol: Properties
39
+
40
+ public var webView: WKWebView? {
41
+ return bridgeDelegate?.bridgedWebView
42
+ }
43
+
44
+ public let autoRegisterPlugins: Bool
45
+
46
+ public var notificationRouter: NotificationRouter
47
+
48
+ public var isSimEnvironment: Bool {
49
+ #if targetEnvironment(simulator)
50
+ return true
51
+ #else
52
+ return false
53
+ #endif
54
+ }
55
+
56
+ public var isDevEnvironment: Bool {
57
+ return CapacitorBridge.isDevEnvironment
58
+ }
59
+
60
+ public var userInterfaceStyle: UIUserInterfaceStyle {
61
+ return viewController?.traitCollection.userInterfaceStyle ?? .unspecified
62
+ }
63
+
64
+ public var statusBarVisible: Bool {
65
+ get {
66
+ return !(viewController?.prefersStatusBarHidden ?? true)
67
+ }
68
+ set {
69
+ DispatchQueue.main.async { [weak self] in
70
+ (self?.viewController as? CAPBridgeViewController)?.setStatusBarVisible(newValue)
71
+ }
72
+ }
73
+ }
74
+
75
+ public var statusBarStyle: UIStatusBarStyle {
76
+ get {
77
+ return viewController?.preferredStatusBarStyle ?? .default
78
+ }
79
+ set {
80
+ DispatchQueue.main.async { [weak self] in
81
+ (self?.viewController as? CAPBridgeViewController)?.setStatusBarStyle(newValue)
82
+ }
83
+ }
84
+ }
85
+
86
+ public var statusBarAnimation: UIStatusBarAnimation {
87
+ get {
88
+ return (viewController as? CAPBridgeViewController)?.statusBarAnimation ?? .slide
89
+ }
90
+ set {
91
+ DispatchQueue.main.async { [weak self] in
92
+ (self?.viewController as? CAPBridgeViewController)?.setStatusBarAnimation(newValue)
93
+ }
94
+ }
95
+ }
96
+
97
+ var tmpWindow: UIWindow?
98
+ static let tmpVCAppeared = Notification(name: Notification.Name(rawValue: "tmpViewControllerAppeared"))
99
+ public static let capacitorSite = "https://capacitorjs.com/"
100
+ public static let fileStartIdentifier = "/_capacitor_file_"
101
+ public static let httpInterceptorStartIdentifier = "/_capacitor_http_interceptor_"
102
+ @available(*, deprecated, message: "`httpsInterceptorStartIdentifier` is no longer required. All proxied requests are handled via `httpInterceptorStartIdentifier` instead")
103
+ public static let httpsInterceptorStartIdentifier = "/_capacitor_https_interceptor_"
104
+ public static let httpInterceptorUrlParam = "u"
105
+ public static let defaultScheme = "capacitor"
106
+
107
+ public private(set) var webViewAssetHandler: WebViewAssetHandler
108
+ public private(set) var webViewDelegationHandler: WebViewDelegationHandler
109
+ public private(set) weak var bridgeDelegate: CAPBridgeDelegate?
110
+ @objc public var viewController: UIViewController? {
111
+ return bridgeDelegate?.bridgedViewController
112
+ }
113
+
114
+ var lastPlugin: CAPPlugin?
115
+
116
+ @objc public var config: InstanceConfiguration
117
+ // Map of all loaded and instantiated plugins by pluginId -> instance
118
+ var plugins = [String: CapacitorPlugin]()
119
+ // Manager for getting Cordova plugins
120
+ var cordovaPluginManager: CDVPluginManager?
121
+ // Calls we are storing to resolve later
122
+ var storedCalls = ConcurrentDictionary<CAPPluginCall>()
123
+ // Whether to inject the Cordova files
124
+ private var injectCordovaFiles = false
125
+ private var cordovaParser: CDVConfigParser?
126
+ private var injectMiscFiles: [String] = []
127
+ private var canInjectJS: Bool = true
128
+
129
+ // Background dispatch queue for plugin calls
130
+ open private(set) var dispatchQueue = DispatchQueue(label: "bridge")
131
+ // Array of block based observers
132
+ var observers: [NSObjectProtocol] = []
133
+
134
+ // MARK: - CAPBridgeProtocol: Deprecated
135
+
136
+ public func getWebView() -> WKWebView? {
137
+ return webView
138
+ }
139
+
140
+ public func isSimulator() -> Bool {
141
+ return isSimEnvironment
142
+ }
143
+
144
+ public func isDevMode() -> Bool {
145
+ return isDevEnvironment
146
+ }
147
+
148
+ public func getStatusBarVisible() -> Bool {
149
+ return statusBarVisible
150
+ }
151
+
152
+ @nonobjc public func setStatusBarVisible(_ visible: Bool) {
153
+ statusBarVisible = visible
154
+ }
155
+
156
+ public func getStatusBarStyle() -> UIStatusBarStyle {
157
+ return statusBarStyle
158
+ }
159
+ @nonobjc public func setStatusBarStyle(_ style: UIStatusBarStyle) {
160
+ statusBarStyle = style
161
+ }
162
+
163
+ public func getUserInterfaceStyle() -> UIUserInterfaceStyle {
164
+ return userInterfaceStyle
165
+ }
166
+
167
+ public func getLocalUrl() -> String {
168
+ return config.localURL.absoluteString
169
+ }
170
+
171
+ @nonobjc public func setStatusBarAnimation(_ animation: UIStatusBarAnimation) {
172
+ statusBarAnimation = animation
173
+ }
174
+
175
+ public func setServerBasePath(_ path: String) {
176
+ let url = URL(fileURLWithPath: path, isDirectory: true)
177
+ guard FileManager.default.fileExists(atPath: url.path) else { return }
178
+ config = config.updatingAppLocation(url)
179
+ webViewAssetHandler.setAssetPath(url.path)
180
+ }
181
+
182
+ // MARK: - Static Methods
183
+
184
+ /**
185
+ Print a hopefully informative error message to the log when something
186
+ particularly dreadful happens.
187
+ */
188
+ static func fatalError(_ error: Error, _ originalError: Error) {
189
+ CAPLog.print("⚡️ ❌ Capacitor: FATAL ERROR")
190
+ CAPLog.print("⚡️ ❌ Error was: ", originalError.localizedDescription)
191
+ switch error {
192
+ case CapacitorBridgeError.errorExportingCoreJS:
193
+ CAPLog.print("⚡️ ❌ Unable to export required Bridge JavaScript. Bridge will not function.")
194
+ CAPLog.print("⚡️ ❌ You should run \"npx capacitor copy\" to ensure the Bridge JS is added to your project.")
195
+ if let wke = originalError as? WKError {
196
+ CAPLog.print("⚡️ ❌ ", wke.userInfo)
197
+ }
198
+ default:
199
+ CAPLog.print("⚡️ ❌ Unknown error")
200
+ }
201
+
202
+ CAPLog.print("⚡️ ❌ Please verify your installation or file an issue")
203
+ }
204
+
205
+ // MARK: - Initialization
206
+
207
+ public init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: CDVConfigParser, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) {
208
+ self.bridgeDelegate = bridgeDelegate
209
+ self.webViewAssetHandler = assetHandler
210
+ self.webViewDelegationHandler = delegationHandler
211
+ self.config = configuration
212
+ self.cordovaParser = cordovaConfiguration
213
+ self.notificationRouter = NotificationRouter()
214
+ self.notificationRouter.handleApplicationNotifications = configuration.handleApplicationNotifications
215
+ self.autoRegisterPlugins = autoRegisterPlugins
216
+ super.init()
217
+
218
+ self.webViewDelegationHandler.bridge = self
219
+
220
+ exportCoreJS(localUrl: configuration.localURL.absoluteString)
221
+ registerPlugins()
222
+ setupCordovaCompatibility()
223
+ exportMiscJS()
224
+ canInjectJS = false
225
+ observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in
226
+ self?.tmpWindow = nil
227
+ })
228
+
229
+ self.setupWebDebugging(configuration: configuration)
230
+ }
231
+
232
+ deinit {
233
+ // the message handler needs to removed to avoid any retain cycles
234
+ webViewDelegationHandler.cleanUp()
235
+ for observer in observers {
236
+ NotificationCenter.default.removeObserver(observer)
237
+ }
238
+ }
239
+
240
+ // MARK: - Plugins
241
+ /**
242
+ Export core JavaScript to the webview
243
+ */
244
+ func exportCoreJS(localUrl: String) {
245
+ do {
246
+ try JSExport.exportCapacitorGlobalJS(userContentController: webViewDelegationHandler.contentController,
247
+ isDebug: isDevEnvironment,
248
+ loggingEnabled: config.loggingEnabled,
249
+ localUrl: localUrl)
250
+ try JSExport.exportBridgeJS(userContentController: webViewDelegationHandler.contentController)
251
+ } catch {
252
+ type(of: self).fatalError(error, error)
253
+ }
254
+ }
255
+
256
+ /**
257
+ Export misc JavaScript to the webview
258
+ */
259
+ func exportMiscJS() {
260
+ JSExport.exportMiscFileJS(paths: injectMiscFiles, userContentController: webViewDelegationHandler.contentController)
261
+ injectMiscFiles.removeAll()
262
+ }
263
+
264
+ /**
265
+ Set up our Cordova compat by loading all known Cordova plugins and injecting their JS.
266
+ */
267
+ func setupCordovaCompatibility() {
268
+ if injectCordovaFiles {
269
+ exportCordovaJS()
270
+ registerCordovaPlugins()
271
+ } else {
272
+ observers.append(NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in
273
+ self?.triggerDocumentJSEvent(eventName: "resume")
274
+ })
275
+ observers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in
276
+ self?.triggerDocumentJSEvent(eventName: "pause")
277
+ })
278
+ }
279
+ }
280
+
281
+ /**
282
+ Export the core Cordova JS runtime
283
+ */
284
+ func exportCordovaJS() {
285
+ do {
286
+ try JSExport.exportCordovaJS(userContentController: webViewDelegationHandler.contentController)
287
+ } catch {
288
+ type(of: self).fatalError(error, error)
289
+ }
290
+ }
291
+
292
+ /**
293
+ Reset the state of the bridge between navigations to avoid
294
+ sending data back to the page from a previous page.
295
+ */
296
+ func reset() {
297
+ storedCalls.withLock { $0.removeAll() }
298
+ removeAllPluginListeners()
299
+ }
300
+
301
+ /**
302
+ Register all plugins that have been declared
303
+ */
304
+ func registerPlugins() {
305
+ var pluginList: [AnyClass] = [CAPHttpPlugin.self, CAPConsolePlugin.self, CAPWebViewPlugin.self, CAPCookiesPlugin.self, CAPSystemBarsPlugin.self]
306
+
307
+ if autoRegisterPlugins {
308
+ do {
309
+ if let pluginJSON = Bundle.main.url(forResource: "capacitor.config", withExtension: "json") {
310
+ let pluginData = try Data(contentsOf: pluginJSON)
311
+ let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData)
312
+
313
+ for plugin in registrationList.packageClassList {
314
+ if let pluginClass = NSClassFromString(plugin) {
315
+ if pluginClass == CDVPlugin.self {
316
+ injectCordovaFiles = true
317
+ } else {
318
+ pluginList.append(pluginClass)
319
+ }
320
+ }
321
+ }
322
+ }
323
+ } catch {
324
+ CAPLog.print("Error registering plugins: \(error)")
325
+ }
326
+ }
327
+
328
+ for plugin in pluginList {
329
+ if plugin is CAPInstancePlugin.Type { continue }
330
+ if let capPlugin = plugin as? CapacitorPlugin.Type {
331
+ registerPlugin(capPlugin)
332
+ }
333
+ }
334
+ }
335
+
336
+ public func registerPluginType(_ pluginType: CAPPlugin.Type) {
337
+ if autoRegisterPlugins { return }
338
+ if pluginType is CAPInstancePlugin.Type {
339
+ Swift.fatalError("""
340
+
341
+ ⚡️ ❌ Cannot register class \(pluginType): CAPInstancePlugin through registerPluginType(_:).
342
+ ⚡️ ❌ Use `registerPluginInstance(_:)` to register subclasses of CAPInstancePlugin.
343
+ """)
344
+ }
345
+ guard let bridgedType = pluginType as? CapacitorPlugin.Type else { return }
346
+ registerPlugin(bridgedType)
347
+ }
348
+
349
+ public func registerPluginInstance(_ pluginInstance: CAPPlugin) {
350
+ guard let pluginInstance = pluginInstance as? CapacitorPlugin else {
351
+ CAPLog.print("""
352
+
353
+ ⚡️ Plugin \(pluginInstance.classForCoder) must conform to CAPBridgedPlugin.
354
+ ⚡️ Not loading plugin \(pluginInstance.classForCoder)
355
+ """)
356
+ return
357
+ }
358
+
359
+ if plugins[pluginInstance.jsName] != nil {
360
+ CAPLog.print("⚡️ Overriding existing registered plugin \(pluginInstance.classForCoder)")
361
+ }
362
+ plugins[pluginInstance.jsName] = pluginInstance
363
+ pluginInstance.load(on: self)
364
+
365
+ JSExport.exportJS(for: pluginInstance, in: webViewDelegationHandler.contentController)
366
+ }
367
+
368
+ /**
369
+ Register a single plugin.
370
+ */
371
+ func registerPlugin(_ pluginType: CapacitorPlugin.Type) {
372
+ if let plugin = loadPlugin(type: pluginType) {
373
+ JSExport.exportJS(for: plugin, in: webViewDelegationHandler.contentController)
374
+ }
375
+ }
376
+
377
+ func loadPlugin(type: CAPPlugin.Type) -> CapacitorPlugin? {
378
+ guard let plugin = type.init() as? CapacitorPlugin else {
379
+ CAPLog.print("⚡️ Unable to load plugin \(type.classForCoder()). No such module found.")
380
+ return nil
381
+ }
382
+ plugin.load(on: self)
383
+ plugins[plugin.jsName] = plugin
384
+ return plugin
385
+ }
386
+
387
+ // MARK: - CAPBridgeProtocol: Plugin Access
388
+
389
+ @objc public func plugin(withName: String) -> CAPPlugin? {
390
+ return self.plugins[withName]
391
+ }
392
+
393
+ // MARK: - CAPBridgeProtocol: Call Management
394
+
395
+ @objc public func saveCall(_ call: CAPPluginCall) {
396
+ storedCalls[call.callbackId] = call
397
+ }
398
+
399
+ @objc public func savedCall(withID: String) -> CAPPluginCall? {
400
+ return storedCalls[withID]
401
+ }
402
+
403
+ @objc public func releaseCall(_ call: CAPPluginCall) {
404
+ releaseCall(withID: call.callbackId)
405
+ }
406
+
407
+ @objc public func releaseCall(withID: String) {
408
+ _ = storedCalls.withLock { $0.removeValue(forKey: withID) }
409
+ }
410
+
411
+ // MARK: - Deprecated Versions
412
+
413
+ @objc public func getSavedCall(_ callbackId: String) -> CAPPluginCall? {
414
+ return savedCall(withID: callbackId)
415
+ }
416
+
417
+ @objc public func releaseCall(callbackId: String) {
418
+ releaseCall(withID: callbackId)
419
+ }
420
+
421
+ // MARK: - Internal
422
+
423
+ func getDispatchQueue() -> DispatchQueue {
424
+ return self.dispatchQueue
425
+ }
426
+
427
+ func registerCordovaPlugins() {
428
+ guard let cordovaParser = cordovaParser else {
429
+ return
430
+ }
431
+ cordovaPluginManager = CDVPluginManager.init(parser: cordovaParser, viewController: self.viewController, webView: self.getWebView())
432
+ if cordovaParser.startupPluginNames.count > 0 {
433
+ for pluginName in cordovaParser.startupPluginNames {
434
+ _ = cordovaPluginManager?.getCommandInstance(pluginName as? String)
435
+ }
436
+ }
437
+ do {
438
+ try JSExport.exportCordovaPluginsJS(userContentController: webViewDelegationHandler.contentController)
439
+ } catch {
440
+ type(of: self).fatalError(error, error)
441
+ }
442
+ }
443
+
444
+ func reload() {
445
+ self.getWebView()?.reload()
446
+ }
447
+
448
+ func docLink(_ url: String) -> String {
449
+ return "\(type(of: self).capacitorSite)docs/\(url)"
450
+ }
451
+
452
+ private func setupWebDebugging(configuration: InstanceConfiguration) {
453
+ let isWebDebuggable = configuration.isWebDebuggable
454
+ if isWebDebuggable, #unavailable(iOS 16.4) {
455
+ CAPLog.print("⚡️ Warning: isWebDebuggable only functions as intended on iOS 16.4 and above.")
456
+ }
457
+
458
+ if #available(iOS 16.4, *) {
459
+ self.webView?.isInspectable = isWebDebuggable
460
+ }
461
+ }
462
+
463
+ /**
464
+ Handle a call from JavaScript. First, find the corresponding plugin, construct a selector,
465
+ and perform that selector on the plugin instance.
466
+
467
+ Quiet the length warning because we don't want to refactor the function at this time.
468
+ */
469
+ // swiftlint:disable:next function_body_length
470
+ func handleJSCall(call: JSCall) {
471
+ let load = {
472
+ NSClassFromString(call.pluginId)
473
+ .flatMap { $0 as? CAPPlugin.Type }
474
+ .flatMap(self.loadPlugin(type:))
475
+ }
476
+
477
+ guard let plugin = plugins[call.pluginId] ?? load() else {
478
+ CAPLog.print("⚡️ Error loading plugin \(call.pluginId) for call. Check that the pluginId is correct")
479
+ return
480
+ }
481
+
482
+ let selector: Selector
483
+ if call.method == "addListener" || call.method == "removeListener" || call.method == "removeAllListeners" {
484
+ selector = NSSelectorFromString(call.method + ":")
485
+ } else {
486
+ guard let method = plugin.getMethod(named: call.method) else {
487
+ CAPLog.print("⚡️ Error calling method \(call.method) on plugin \(call.pluginId): No method found.")
488
+ CAPLog.print("⚡️ Ensure plugin method exists and uses @objc in its declaration, and has been defined")
489
+ return
490
+ }
491
+
492
+ selector = method.selector
493
+ }
494
+
495
+ if !plugin.responds(to: selector) {
496
+ // we don't want to break up string literals
497
+ // swiftlint:disable line_length
498
+ CAPLog.print("⚡️ Error: Plugin \(plugin.getId()) does not respond to method call \"\(call.method)\" using selector \"\(selector)\".")
499
+ CAPLog.print("⚡️ Ensure plugin method exists, uses @objc in its declaration, and arguments match selector without callbacks in CAP_PLUGIN_METHOD.")
500
+ CAPLog.print("⚡️ Learn more: \(docLink(DocLinks.CAPPluginMethodSelector.rawValue))")
501
+ // swiftlint:enable line_length
502
+ return
503
+ }
504
+
505
+ // Create a plugin call object and handle the success/error callbacks
506
+ dispatchQueue.async { [weak self] in
507
+ // let startTime = CFAbsoluteTimeGetCurrent()
508
+
509
+ let pluginCall = CAPPluginCall(callbackId: call.callbackId, methodName: call.method,
510
+ options: JSTypes.coerceDictionaryToJSObject(call.options,
511
+ formattingDatesAsStrings: plugin.shouldStringifyDatesInCalls) ?? [:],
512
+ success: {(result: CAPPluginCallResult?, pluginCall: CAPPluginCall?) in
513
+ if let result = result {
514
+ self?.toJs(result: JSResult(call: call, callResult: result), save: pluginCall?.keepAlive ?? false)
515
+ } else {
516
+ self?.toJs(result: JSResult(call: call, result: .dictionary([:])), save: pluginCall?.keepAlive ?? false)
517
+ }
518
+ }, error: {(error: CAPPluginCallError?) in
519
+ if let error = error {
520
+ self?.toJsError(error: JSResultError(call: call, callError: error))
521
+ } else {
522
+ self?.toJsError(error: JSResultError(call: call,
523
+ errorMessage: "",
524
+ errorDescription: "",
525
+ errorCode: nil,
526
+ result: .dictionary([:])))
527
+ }
528
+ })
529
+
530
+ if let pluginCall = pluginCall {
531
+ plugin.perform(selector, with: pluginCall)
532
+ if pluginCall.keepAlive {
533
+ self?.saveCall(pluginCall)
534
+ }
535
+ }
536
+
537
+ // let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
538
+ // CAPLog.print("Native call took", timeElapsed)
539
+ }
540
+ }
541
+
542
+ /**
543
+ Handle a Cordova call from JavaScript. First, find the corresponding plugin,
544
+ construct a selector, and perform that selector on the plugin instance.
545
+ */
546
+ func handleCordovaJSCall(call: JSCall) {
547
+ // Create a selector to send to the plugin
548
+
549
+ if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) {
550
+ let selector = NSSelectorFromString("\(call.method):")
551
+ if !plugin.responds(to: selector) {
552
+ CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).")
553
+ CAPLog.print("Ensure plugin method exists and uses @objc in its declaration")
554
+ return
555
+ }
556
+
557
+ let arguments: [Any] = call.options["options"] as? [Any] ?? []
558
+ let pluginCall = CDVInvokedUrlCommand(arguments: arguments,
559
+ callbackId: call.callbackId,
560
+ className: plugin.className,
561
+ methodName: call.method)
562
+ plugin.perform(selector, with: pluginCall)
563
+
564
+ } else {
565
+ CAPLog.print("Error: Cordova Plugin mapping not found")
566
+ return
567
+ }
568
+ }
569
+
570
+ func removeAllPluginListeners() {
571
+ for plugin in plugins.values {
572
+ plugin.perform(#selector(CAPPlugin.removeAllListeners(_:)), with: nil)
573
+ }
574
+ }
575
+
576
+ /**
577
+ Send a successful result to the JavaScript layer.
578
+ */
579
+ func toJs(result: JSResultProtocol, save: Bool) {
580
+ let resultJson = result.jsonPayload()
581
+ CAPLog.print("⚡️ TO JS", resultJson.prefix(256))
582
+
583
+ DispatchQueue.main.async {
584
+ self.webView?.evaluateJavaScript("""
585
+ window.Capacitor.fromNative({
586
+ callbackId: '\(result.callbackID)',
587
+ pluginId: '\(result.pluginID)',
588
+ methodName: '\(result.methodName)',
589
+ save: \(save),
590
+ success: true,
591
+ data: \(resultJson)
592
+ })
593
+ """) { (_, error) in
594
+ if let error = error {
595
+ CAPLog.print(error)
596
+ }
597
+ }
598
+ }
599
+ }
600
+
601
+ /**
602
+ Send an error result to the JavaScript layer.
603
+ */
604
+ func toJsError(error: JSResultProtocol) {
605
+ DispatchQueue.main.async {
606
+ self.webView?.evaluateJavaScript("window.Capacitor.fromNative({ callbackId: '\(error.callbackID)', pluginId: '\(error.pluginID)', methodName: '\(error.methodName)', success: false, error: \(error.jsonPayload())})") { (_, error) in
607
+ if let error = error {
608
+ CAPLog.print(error)
609
+ }
610
+ }
611
+ }
612
+ }
613
+
614
+ // MARK: - CAPBridgeProtocol: JavaScript Handling
615
+
616
+ /**
617
+ Inject JavaScript from an external file before the WebView loads.
618
+
619
+ `path` is relative to the public folder
620
+ */
621
+ public func injectScriptBeforeLoad(path: String) {
622
+ if canInjectJS {
623
+ injectMiscFiles.append(path)
624
+ }
625
+ }
626
+
627
+ /**
628
+ Eval JS for a specific plugin.
629
+
630
+ `js` is a short name but needs to be preserved for backwards compatibility.
631
+ */
632
+ // swiftlint:disable:next identifier_name
633
+ @objc public func evalWithPlugin(_ plugin: CAPPlugin, js: String) {
634
+ let wrappedJs = """
635
+ window.Capacitor.withPlugin('\(plugin.getId())', function(plugin) {
636
+ if(!plugin) { console.error('Unable to execute JS in plugin, no such plugin found for id \(plugin.getId())'); }
637
+ \(js)
638
+ });
639
+ """
640
+
641
+ DispatchQueue.main.async {
642
+ self.getWebView()?.evaluateJavaScript(wrappedJs, completionHandler: { (_, error) in
643
+ if let error = error {
644
+ CAPLog.print("⚡️ JS Eval error", error.localizedDescription)
645
+ }
646
+ })
647
+ }
648
+ }
649
+
650
+ /**
651
+ Eval JS in the web view
652
+
653
+ `js` is a short name but needs to be preserved for backwards compatibility.
654
+ */
655
+ // swiftlint:disable:next identifier_name
656
+ @objc public func eval(js: String) {
657
+ DispatchQueue.main.async {
658
+ self.getWebView()?.evaluateJavaScript(js, completionHandler: { (_, error) in
659
+ if let error = error {
660
+ CAPLog.print("⚡️ JS Eval error", error.localizedDescription)
661
+ }
662
+ })
663
+ }
664
+ }
665
+
666
+ @objc public func triggerJSEvent(eventName: String, target: String) {
667
+ self.eval(js: "window.Capacitor.triggerEvent('\(eventName)', '\(target)')")
668
+ }
669
+
670
+ @objc public func triggerJSEvent(eventName: String, target: String, data: String) {
671
+ self.eval(js: "window.Capacitor.triggerEvent('\(eventName)', '\(target)', \(data))")
672
+ }
673
+
674
+ @objc public func triggerWindowJSEvent(eventName: String) {
675
+ self.triggerJSEvent(eventName: eventName, target: "window")
676
+ }
677
+
678
+ @objc public func triggerWindowJSEvent(eventName: String, data: String) {
679
+ self.triggerJSEvent(eventName: eventName, target: "window", data: data)
680
+ }
681
+
682
+ @objc public func triggerDocumentJSEvent(eventName: String) {
683
+ self.triggerJSEvent(eventName: eventName, target: "document")
684
+ }
685
+
686
+ @objc public func triggerDocumentJSEvent(eventName: String, data: String) {
687
+ self.triggerJSEvent(eventName: eventName, target: "document", data: data)
688
+ }
689
+
690
+ public func logToJs(_ message: String, _ level: String = "log") {
691
+ DispatchQueue.main.async {
692
+ self.getWebView()?.evaluateJavaScript("window.Capacitor.logJs('\(message)', '\(level)')") { (result, error) in
693
+ if error != nil, let result = result {
694
+ CAPLog.print(result)
695
+ }
696
+ }
697
+ }
698
+ }
699
+
700
+ // MARK: - CAPBridgeProtocol: Paths, Files, Assets
701
+
702
+ /**
703
+ Translate a URL from the web view into a file URL for native iOS.
704
+
705
+ The web view may be handling several different types of URLs:
706
+ - res:// (shortcut scheme to web assets)
707
+ - file:// (fully qualified URL to file on the local device)
708
+ - base64:// (to be implemented)
709
+ - [web view scheme]:// (already converted once to load in the web view, to be implemented)
710
+ */
711
+ public func localURL(fromWebURL webURL: URL?) -> URL? {
712
+ guard let inputURL = webURL else {
713
+ return nil
714
+ }
715
+
716
+ let url: URL
717
+
718
+ switch inputURL.scheme {
719
+ case "res":
720
+ url = config.appLocation.appendingPathComponent(inputURL.path)
721
+ case "file":
722
+ url = inputURL
723
+ default:
724
+ return nil
725
+ }
726
+
727
+ return url
728
+ }
729
+
730
+ /**
731
+ Translate a file URL for native iOS into a URL to load in the web view.
732
+ */
733
+ public func portablePath(fromLocalURL localURL: URL?) -> URL? {
734
+ guard let inputURL = localURL else {
735
+ return nil
736
+ }
737
+
738
+ return self.config.localURL.appendingPathComponent(CapacitorBridge.fileStartIdentifier).appendingPathComponent(inputURL.path)
739
+ }
740
+
741
+ // MARK: - CAPBridgeProtocol: View Presentation
742
+
743
+ @objc open func showAlertWith(title: String, message: String, buttonTitle: String) {
744
+ let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
745
+ alert.addAction(UIAlertAction(title: buttonTitle, style: UIAlertAction.Style.default, handler: nil))
746
+ self.viewController?.present(alert, animated: true, completion: nil)
747
+ }
748
+
749
+ @objc open func presentVC(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
750
+ if viewControllerToPresent.modalPresentationStyle == .popover {
751
+ self.viewController?.present(viewControllerToPresent, animated: flag, completion: completion)
752
+ } else {
753
+ self.tmpWindow = UIWindow.init(frame: UIScreen.main.bounds)
754
+ self.tmpWindow?.rootViewController = TmpViewController.init()
755
+ self.tmpWindow?.makeKeyAndVisible()
756
+ self.tmpWindow?.rootViewController?.present(viewControllerToPresent, animated: flag, completion: completion)
757
+ }
758
+ }
759
+
760
+ @objc open func dismissVC(animated flag: Bool, completion: (() -> Void)? = nil) {
761
+ if self.tmpWindow == nil {
762
+ self.viewController?.dismiss(animated: flag, completion: completion)
763
+ } else {
764
+ self.tmpWindow?.rootViewController?.dismiss(animated: flag, completion: completion)
765
+ self.tmpWindow = nil
766
+ }
767
+ }
768
+ }