@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,361 @@
1
+ import Foundation
2
+ import WebKit
3
+
4
+ // adopting a public protocol in an internal class is by design
5
+ // swiftlint:disable lower_acl_than_parent
6
+ @objc(CAPWebViewDelegationHandler)
7
+ open class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler, UIScrollViewDelegate {
8
+ public internal(set) weak var bridge: CapacitorBridge?
9
+ open fileprivate(set) var contentController = WKUserContentController()
10
+ enum WebViewLoadingState {
11
+ case unloaded
12
+ case initialLoad(isOpaque: Bool)
13
+ case subsequentLoad
14
+ }
15
+
16
+ fileprivate(set) var webViewLoadingState = WebViewLoadingState.unloaded
17
+
18
+ private let handlerName = "bridge"
19
+
20
+ override public init() {
21
+ super.init()
22
+ contentController.add(self, name: handlerName)
23
+ }
24
+
25
+ open func cleanUp() {
26
+ contentController.removeScriptMessageHandler(forName: handlerName)
27
+ }
28
+
29
+ open func willLoadWebview(_ webView: WKWebView?) {
30
+ // Set the webview to be not opaque on the inital load. This prevents
31
+ // the webview from showing a white background, which is its default
32
+ // loading display, as that can appear as a screen flash. The opacity
33
+ // might have been set by something else, like a plugin, so we want
34
+ // to save the current value so it can be reset on success or failure.
35
+ if let webView = webView, case .unloaded = webViewLoadingState {
36
+ webViewLoadingState = .initialLoad(isOpaque: webView.isOpaque)
37
+ webView.isOpaque = false
38
+ }
39
+ }
40
+
41
+ // MARK: - WKNavigationDelegate
42
+
43
+ // The force unwrap is part of the protocol declaration, so we should keep it.
44
+ // swiftlint:disable:next implicitly_unwrapped_optional
45
+ open func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
46
+ // Reset the bridge on each navigation
47
+ bridge?.reset()
48
+ }
49
+
50
+ open func webView(
51
+ _ webView: WKWebView,
52
+ requestMediaCapturePermissionFor origin: WKSecurityOrigin,
53
+ initiatedByFrame frame: WKFrameInfo,
54
+ type: WKMediaCaptureType,
55
+ decisionHandler: @escaping (WKPermissionDecision) -> Void
56
+ ) {
57
+ decisionHandler(.grant)
58
+ }
59
+
60
+ open func webView(_ webView: WKWebView,
61
+ requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin,
62
+ initiatedByFrame frame: WKFrameInfo,
63
+ decisionHandler: @escaping (WKPermissionDecision) -> Void) {
64
+ decisionHandler(.grant)
65
+ }
66
+
67
+ open func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
68
+ // post a notification for any listeners
69
+ NotificationCenter.default.post(name: .capacitorDecidePolicyForNavigationAction, object: navigationAction)
70
+
71
+ // sanity check, these shouldn't ever be nil in practice
72
+ guard let bridge = bridge, let navURL = navigationAction.request.url else {
73
+ decisionHandler(.allow)
74
+ return
75
+ }
76
+
77
+ // first, give plugins the chance to handle the decision
78
+ for pluginObject in bridge.plugins {
79
+ let plugin = pluginObject.value
80
+ let selector = NSSelectorFromString("shouldOverrideLoad:")
81
+ if plugin.responds(to: selector) {
82
+ let shouldOverrideLoad = plugin.shouldOverrideLoad(navigationAction)
83
+ if shouldOverrideLoad != nil {
84
+ if shouldOverrideLoad == true {
85
+ decisionHandler(.cancel)
86
+ return
87
+ } else if shouldOverrideLoad == false {
88
+ decisionHandler(.allow)
89
+ return
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ // next, check if this is covered by the allowedNavigation configuration
96
+ if let host = navURL.host, bridge.config.shouldAllowNavigation(to: host) {
97
+ decisionHandler(.allow)
98
+ return
99
+ }
100
+
101
+ // otherwise, is this a new window or a main frame navigation but to an outside source
102
+ let toplevelNavigation = (navigationAction.targetFrame == nil || navigationAction.targetFrame?.isMainFrame == true)
103
+
104
+ // Check if the url being navigated to is configured as an application url (whether local or remote)
105
+ let isApplicationNavigation = navURL.absoluteString.starts(with: bridge.config.serverURL.absoluteString) ||
106
+ navURL.absoluteString.starts(with: bridge.config.localURL.absoluteString)
107
+
108
+ if !isApplicationNavigation, toplevelNavigation {
109
+ // disallow and let the system handle it
110
+ if UIApplication.shared.applicationState == .active {
111
+ UIApplication.shared.open(navURL, options: [:], completionHandler: nil)
112
+ }
113
+ decisionHandler(.cancel)
114
+ return
115
+ }
116
+
117
+ // fallthrough to allowing it
118
+ decisionHandler(.allow)
119
+ }
120
+
121
+ // The force unwrap is part of the protocol declaration, so we should keep it.
122
+ // swiftlint:disable:next implicitly_unwrapped_optional
123
+ open func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
124
+ if case .initialLoad(let isOpaque) = webViewLoadingState {
125
+ webView.isOpaque = isOpaque
126
+ webViewLoadingState = .subsequentLoad
127
+ }
128
+ CAPLog.print("⚡️ WebView loaded")
129
+ }
130
+
131
+ // The force unwrap is part of the protocol declaration, so we should keep it.
132
+ // swiftlint:disable:next implicitly_unwrapped_optional
133
+ open func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
134
+ if case .initialLoad(let isOpaque) = webViewLoadingState {
135
+ webView.isOpaque = isOpaque
136
+ webViewLoadingState = .subsequentLoad
137
+ }
138
+
139
+ if let errorURL = bridge?.config.errorPathURL {
140
+ webView.load(URLRequest(url: errorURL))
141
+ }
142
+
143
+ CAPLog.print("⚡️ WebView failed to load")
144
+ CAPLog.print("⚡️ Error: " + error.localizedDescription)
145
+ }
146
+
147
+ // The force unwrap is part of the protocol declaration, so we should keep it.
148
+ // swiftlint:disable:next implicitly_unwrapped_optional
149
+ open func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
150
+ if let errorURL = bridge?.config.errorPathURL {
151
+ webView.load(URLRequest(url: errorURL))
152
+ }
153
+
154
+ CAPLog.print("⚡️ WebView failed provisional navigation")
155
+ CAPLog.print("⚡️ Error: " + error.localizedDescription)
156
+ }
157
+
158
+ open func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
159
+ CAPLog.print("⚡️ WebView process terminated")
160
+ bridge?.reset()
161
+ webView.reload()
162
+ }
163
+
164
+ open func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping @MainActor (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
165
+ guard let bridge = bridge else {
166
+ completionHandler(.rejectProtectionSpace, nil)
167
+ return
168
+ }
169
+
170
+ for pluginObject in bridge.plugins {
171
+ let plugin = pluginObject.value
172
+ let selector = NSSelectorFromString("handleWKWebViewURLAuthenticationChallenge:completionHandler:")
173
+ if plugin.responds(to: selector) {
174
+ if plugin.handleWKWebViewURLAuthenticationChallenge(challenge, completionHandler: completionHandler) {
175
+ return
176
+ }
177
+ }
178
+ }
179
+
180
+ completionHandler(.rejectProtectionSpace, nil)
181
+ return
182
+ }
183
+
184
+ // MARK: - WKScriptMessageHandler
185
+
186
+ open func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
187
+ guard let bridge = bridge else {
188
+ return
189
+ }
190
+
191
+ let body = message.body
192
+ if let dict = body as? [String: Any] {
193
+ let type = dict["type"] as? String ?? ""
194
+
195
+ if type == "js.error" {
196
+ if let error = dict["error"] as? [String: Any] {
197
+ logJSError(error)
198
+ }
199
+ } else if type == "message" {
200
+ let pluginId = dict["pluginId"] as? String ?? ""
201
+ let method = dict["methodName"] as? String ?? ""
202
+ let callbackId = dict["callbackId"] as? String ?? ""
203
+
204
+ let options = dict["options"] as? [String: Any] ?? [:]
205
+
206
+ if pluginId != "Console" {
207
+ CAPLog.print("⚡️ To Native -> ", pluginId, method, callbackId)
208
+ }
209
+
210
+ bridge.handleJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId))
211
+ } else if type == "cordova" {
212
+ let pluginId = dict["service"] as? String ?? ""
213
+ let method = dict["action"] as? String ?? ""
214
+ let callbackId = dict["callbackId"] as? String ?? ""
215
+
216
+ let args = dict["actionArgs"] as? Array ?? []
217
+ let options = ["options": args]
218
+
219
+ CAPLog.print("To Native Cordova -> ", pluginId, method, callbackId, options)
220
+
221
+ bridge.handleCordovaJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId))
222
+ }
223
+ }
224
+ }
225
+
226
+ // MARK: - WKUIDelegate
227
+
228
+ open func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
229
+ guard var viewController = bridge?.viewController else {
230
+ completionHandler()
231
+ return
232
+ }
233
+
234
+ if let presentedVC = viewController.presentedViewController, !presentedVC.isBeingDismissed {
235
+ viewController = presentedVC
236
+ }
237
+
238
+ let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
239
+
240
+ alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
241
+ completionHandler()
242
+ }))
243
+
244
+ viewController.present(alertController, animated: true, completion: nil)
245
+ }
246
+
247
+ open func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
248
+ guard let viewController = bridge?.viewController else {
249
+ return
250
+ }
251
+
252
+ let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
253
+
254
+ alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (_) in
255
+ completionHandler(false)
256
+ }))
257
+
258
+ alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
259
+ completionHandler(true)
260
+ }))
261
+
262
+ viewController.present(alertController, animated: true, completion: nil)
263
+ }
264
+
265
+ open func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
266
+
267
+ // Check if this is synchronous cookie or http call
268
+ do {
269
+ if let dataFromString = prompt.data(using: .utf8, allowLossyConversion: false) {
270
+ if let payload = try JSONSerialization.jsonObject(with: dataFromString, options: .fragmentsAllowed) as? [String: AnyObject] {
271
+ let type = payload["type"] as? String
272
+
273
+ if type == "CapacitorCookies.get" {
274
+ completionHandler(CapacitorCookieManager(bridge!.config).getCookies())
275
+ // Don't present prompt
276
+ return
277
+ } else if type == "CapacitorCookies.set" {
278
+ // swiftlint:disable force_cast
279
+ let action = payload["action"] as! String
280
+ let domain = payload["domain"] as! String
281
+ CapacitorCookieManager(bridge!.config).setCookie(domain, action)
282
+ completionHandler("")
283
+ // swiftlint:enable force_cast
284
+ // Don't present prompt
285
+ return
286
+ } else if type == "CapacitorCookies.isEnabled" {
287
+ let pluginConfig = bridge!.config.getPluginConfig("CapacitorCookies")
288
+ completionHandler(String(pluginConfig.getBoolean("enabled", false)))
289
+ // Don't present prompt
290
+ return
291
+ } else if type == "CapacitorHttp" {
292
+ let pluginConfig = bridge!.config.getPluginConfig("CapacitorHttp")
293
+ completionHandler(String(pluginConfig.getBoolean("enabled", false)))
294
+ // Don't present prompt
295
+ return
296
+ }
297
+ }
298
+ }
299
+ } catch {
300
+ // Continue with regular prompt
301
+ }
302
+
303
+ guard let viewController = bridge?.viewController else {
304
+ return
305
+ }
306
+
307
+ let alertController = UIAlertController(title: nil, message: prompt, preferredStyle: .alert)
308
+
309
+ alertController.addTextField { (textField) in
310
+ textField.text = defaultText
311
+ }
312
+
313
+ alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (_) in
314
+ completionHandler(nil)
315
+ }))
316
+
317
+ alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
318
+ if let text = alertController.textFields?.first?.text {
319
+ completionHandler(text)
320
+ } else {
321
+ completionHandler(defaultText)
322
+ }
323
+ }))
324
+
325
+ viewController.present(alertController, animated: true, completion: nil)
326
+ }
327
+
328
+ open func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
329
+ if let url = navigationAction.request.url {
330
+ UIApplication.shared.open(url, options: [:], completionHandler: nil)
331
+ }
332
+ return nil
333
+ }
334
+
335
+ // MARK: - UIScrollViewDelegate
336
+
337
+ // disable zooming in WKWebView ScrollView
338
+ open func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
339
+ scrollView.pinchGestureRecognizer?.isEnabled = false
340
+ }
341
+
342
+ // MARK: - Private
343
+
344
+ private func logJSError(_ error: [String: Any]) {
345
+ let message = error["message"] ?? "No message"
346
+ let url = error["url"] as? String ?? ""
347
+ let line = error["line"] ?? ""
348
+ let col = error["col"] ?? ""
349
+ var filename = ""
350
+ if let filenameIndex = url.range(of: "/", options: .backwards)?.lowerBound {
351
+ let index = url.index(after: filenameIndex)
352
+ filename = String(url[index...])
353
+ }
354
+
355
+ CAPLog.print("\n⚡️ ------ STARTUP JS ERROR ------\n")
356
+ CAPLog.print("⚡️ \(message)")
357
+ CAPLog.print("⚡️ URL: \(url)")
358
+ CAPLog.print("⚡️ \(filename):\(line):\(col)")
359
+ CAPLog.print("\n⚡️ See above for help with debugging blank-screen issues")
360
+ }
361
+ }