@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,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ </dict>
24
+ </plist>
@@ -0,0 +1,122 @@
1
+ import Foundation
2
+
3
+ public class JSDate {
4
+ @available(*, deprecated, message: "No longer needed. Dates will be mapped to strings during serialization.")
5
+ static func toString(_ date: Date) -> String {
6
+ let formatter = ISO8601DateFormatter()
7
+ return formatter.string(from: date)
8
+ }
9
+ }
10
+
11
+ @available(*, deprecated, renamed: "PluginCallResultData")
12
+ public typealias JSResultBody = [String: Any]
13
+
14
+ /**
15
+ * A call originating from JavaScript land
16
+ */
17
+ internal struct JSCall {
18
+ let options: [String: Any]
19
+ let pluginId: String
20
+ let method: String
21
+ let callbackId: String
22
+ }
23
+
24
+ internal protocol JSResultProtocol {
25
+ var call: JSCall { get }
26
+ var callbackID: String { get }
27
+ var pluginID: String { get }
28
+ var methodName: String { get }
29
+ func jsonPayload() -> String
30
+ }
31
+
32
+ internal extension JSResultProtocol {
33
+ var callbackID: String {
34
+ return call.callbackId
35
+ }
36
+
37
+ var pluginID: String {
38
+ return call.pluginId
39
+ }
40
+
41
+ var methodName: String {
42
+ return call.method
43
+ }
44
+ }
45
+
46
+ private enum SerializationResult: String {
47
+ case undefined = "undefined"
48
+ case empty = "{}"
49
+ }
50
+
51
+ /**
52
+ * A result of processing a JSCall, contains
53
+ * a reference to the original call and the new result.
54
+ */
55
+
56
+ internal struct JSResult: JSResultProtocol {
57
+ let call: JSCall
58
+ let result: PluginCallResult?
59
+
60
+ func jsonPayload() -> String {
61
+ guard let result = result else {
62
+ return SerializationResult.undefined.rawValue
63
+ }
64
+ do {
65
+ if let payload = try result.jsonRepresentation() {
66
+ return payload
67
+ }
68
+ } catch PluginCallResult.SerializationError.invalidObject {
69
+ CAPLog.print("[Capacitor Plugin Error] - \(call.pluginId) - \(call.method) - Unable to serialize plugin response as JSON." +
70
+ "Ensure that all data passed to success callback from module method is JSON serializable!")
71
+ } catch {
72
+ CAPLog.print("Unable to serialize plugin response as JSON: \(error.localizedDescription)")
73
+ }
74
+ return SerializationResult.empty.rawValue
75
+ }
76
+ }
77
+
78
+ internal extension JSResult {
79
+ init(call: JSCall, callResult: CAPPluginCallResult) {
80
+ self.call = call
81
+ self.result = callResult.resultData
82
+ }
83
+ }
84
+
85
+ internal struct JSResultError: JSResultProtocol {
86
+ let call: JSCall
87
+ let errorMessage: String
88
+ let errorDescription: String
89
+ let errorCode: String?
90
+ let result: PluginCallResult
91
+
92
+ func jsonPayload() -> String {
93
+ var errorDictionary: [String: Any] = [
94
+ "message": self.errorMessage,
95
+ "errorMessage": self.errorMessage
96
+ ]
97
+ errorDictionary["code"] = self.errorCode
98
+
99
+ do {
100
+ if let payload = try result.jsonRepresentation(includingFields: errorDictionary) {
101
+ CAPLog.print("ERROR MESSAGE: ", payload.prefix(512))
102
+ return payload
103
+ }
104
+ } catch PluginCallResult.SerializationError.invalidObject {
105
+ CAPLog.print("[Capacitor Plugin Error] - \(call.pluginId) - \(call.method) - Unable to serialize plugin response as JSON." +
106
+ "Ensure that all data passed to success callback from module method is JSON serializable!")
107
+ } catch {
108
+ CAPLog.print("Unable to serialize plugin response as JSON: \(error.localizedDescription)")
109
+ }
110
+ return SerializationResult.empty.rawValue
111
+ }
112
+ }
113
+
114
+ internal extension JSResultError {
115
+ init(call: JSCall, callError: CAPPluginCallError) {
116
+ self.call = call
117
+ errorMessage = callError.message
118
+ errorDescription = callError.error?.localizedDescription ?? ""
119
+ errorCode = callError.code
120
+ result = callError.resultData ?? .dictionary([:])
121
+ }
122
+ }
@@ -0,0 +1,218 @@
1
+ internal struct PluginHeaderMethod: Codable {
2
+ let name: String
3
+ let rtype: String?
4
+ }
5
+
6
+ internal struct PluginHeader: Codable {
7
+ let name: String
8
+ let methods: [PluginHeaderMethod]
9
+ }
10
+
11
+ /**
12
+ JSExport handles defining JS APIs that map to registered plugins and are responsible for proxying calls to our bridge.
13
+ */
14
+ internal class JSExport {
15
+ static let catchallOptionsParameter = "_options"
16
+ static let callbackParameter = "_callback"
17
+
18
+ static func exportCapacitorGlobalJS(userContentController: WKUserContentController, isDebug: Bool, loggingEnabled: Bool, localUrl: String) throws {
19
+ let data = "window.Capacitor = { DEBUG: \(isDebug), isLoggingEnabled: \(loggingEnabled), Plugins: {} }; window.WEBVIEW_SERVER_URL = '\(localUrl)';"
20
+ let userScript = WKUserScript(source: data, injectionTime: .atDocumentStart, forMainFrameOnly: true)
21
+ userContentController.addUserScript(userScript)
22
+ }
23
+
24
+ static func exportBridgeJS(userContentController: WKUserContentController) throws {
25
+ let capBundle = Bundle(for: Self.self)
26
+ guard let jsUrl = capBundle.url(forResource: "native-bridge", withExtension: "js") else {
27
+ CAPLog.print("ERROR: Required native-bridge.js file in Capacitor not found. Bridge will not function!")
28
+ throw CapacitorBridgeError.errorExportingCoreJS
29
+ }
30
+ do {
31
+ try self.injectFile(fileURL: jsUrl, userContentController: userContentController)
32
+ } catch {
33
+ CAPLog.print("ERROR: Unable to read required native-bridge.js file from the Capacitor framework. Bridge will not function!")
34
+ throw CapacitorBridgeError.errorExportingCoreJS
35
+ }
36
+ }
37
+
38
+ static func exportCordovaJS(userContentController: WKUserContentController) throws {
39
+ guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else {
40
+ CAPLog.print("ERROR: Required cordova.js file not found. Cordova plugins will not function!")
41
+ throw CapacitorBridgeError.errorExportingCoreJS
42
+ }
43
+ guard let cordovaPluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else {
44
+ CAPLog.print("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!")
45
+ throw CapacitorBridgeError.errorExportingCoreJS
46
+ }
47
+ do {
48
+ try self.injectFile(fileURL: cordovaUrl, userContentController: userContentController)
49
+ try self.injectFile(fileURL: cordovaPluginsUrl, userContentController: userContentController)
50
+ } catch {
51
+ CAPLog.print("ERROR: Unable to read required cordova files. Cordova plugins will not function!")
52
+ throw CapacitorBridgeError.errorExportingCoreJS
53
+ }
54
+ }
55
+
56
+ static func exportMiscFileJS(paths: [String], userContentController: WKUserContentController) {
57
+ for path in paths {
58
+ if let miscJSFilePath = Bundle.main.url(forResource: "public/\(path.replacingOccurrences(of: ".js", with: ""))", withExtension: "js") {
59
+ do {
60
+ try self.injectFile(fileURL: miscJSFilePath, userContentController: userContentController)
61
+ } catch {
62
+ CAPLog.print("WARNING: Unable to inject js from path \(miscJSFilePath)")
63
+ }
64
+ } else {
65
+ CAPLog.print("WARNING: Unable to inject js from path \(path)")
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ Export the JS required to implement the given plugin.
72
+ */
73
+ static func exportJS(for plugin: CapacitorPlugin, in userContentController: WKUserContentController) {
74
+ var lines = [String]()
75
+
76
+ lines.append("""
77
+ (function(w) {
78
+ var a = (w.Capacitor = w.Capacitor || {});
79
+ var p = (a.Plugins = a.Plugins || {});
80
+ var t = (p['\(plugin.jsName)'] = {});
81
+ t.addListener = function(eventName, callback) {
82
+ return w.Capacitor.addListener('\(plugin.jsName)', eventName, callback);
83
+ }
84
+ t.removeAllListeners = function() {
85
+ return w.Capacitor.nativePromise('\(plugin.jsName)', 'removeAllListeners');
86
+ }
87
+ """)
88
+
89
+ for method in plugin.pluginMethods {
90
+ lines.append(generateMethod(pluginClassName: plugin.jsName, method: method))
91
+ }
92
+
93
+ lines.append("""
94
+ })(window);
95
+ """)
96
+ if let data = try? JSONEncoder().encode(createPluginHeader(for: plugin)),
97
+ let header = String(data: data, encoding: .utf8) {
98
+ lines.append("""
99
+ (function(w) {
100
+ var a = (w.Capacitor = w.Capacitor || {});
101
+ var h = (a.PluginHeaders = a.PluginHeaders || []);
102
+ h.push(\(header));
103
+ })(window);
104
+ """)
105
+ }
106
+ let js = lines.joined(separator: "\n")
107
+ let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: true)
108
+ userContentController.addUserScript(userScript)
109
+ }
110
+
111
+ private static func createPluginHeader(for plugin: CapacitorPlugin) -> PluginHeader? {
112
+ let methods = [
113
+ PluginHeaderMethod(name: "addListener", rtype: nil),
114
+ PluginHeaderMethod(name: "removeListener", rtype: nil),
115
+ PluginHeaderMethod(name: "removeAllListeners", rtype: "promise"),
116
+ PluginHeaderMethod(name: "checkPermissions", rtype: "promise"),
117
+ PluginHeaderMethod(name: "requestPermissions", rtype: "promise")
118
+ ]
119
+
120
+ return PluginHeader(
121
+ name: plugin.jsName,
122
+ methods: methods + plugin.pluginMethods.map(createPluginHeaderMethod)
123
+ )
124
+
125
+ }
126
+
127
+ private static func createPluginHeaderMethod(method: CAPPluginMethod) -> PluginHeaderMethod {
128
+ var rtype = method.returnType
129
+ if rtype == "none" {
130
+ rtype = nil
131
+ }
132
+ return PluginHeaderMethod(name: method.name, rtype: rtype)
133
+ }
134
+
135
+ private static func generateMethod(pluginClassName: String, method: CAPPluginMethod) -> String {
136
+ let methodName = method.name!
137
+ let returnType = method.returnType!
138
+ var paramList = [String]()
139
+
140
+ // add the catch-all
141
+ // options argument which takes a full object and converts each
142
+ // key/value pair into an option for plugin call.
143
+ paramList.append(catchallOptionsParameter)
144
+
145
+ // Automatically add the _callback param if returning data through a callback
146
+ if returnType == CAPPluginReturnCallback {
147
+ paramList.append(callbackParameter)
148
+ }
149
+
150
+ // Create a param string of the form "param1, param2, param3"
151
+ let paramString = paramList.joined(separator: ", ")
152
+
153
+ // Generate the argument object that will be sent on each call
154
+ let argObjectString = catchallOptionsParameter
155
+
156
+ var lines = [String]()
157
+
158
+ // Create the function declaration
159
+ lines.append("t['\(method.name!)'] = function(\(paramString)) {")
160
+
161
+ // Create the call to Capacitor ...
162
+ if returnType == CAPPluginReturnNone {
163
+ // ...using none
164
+ lines.append("""
165
+ return w.Capacitor.nativeCallback('\(pluginClassName)', '\(methodName)', \(argObjectString));
166
+ """)
167
+ } else if returnType == CAPPluginReturnPromise {
168
+
169
+ // ...using a promise
170
+ lines.append("""
171
+ return w.Capacitor.nativePromise('\(pluginClassName)', '\(methodName)', \(argObjectString));
172
+ """)
173
+ } else if returnType == CAPPluginReturnCallback {
174
+ // ...using a callback
175
+ lines.append("""
176
+ return w.Capacitor.nativeCallback('\(pluginClassName)', '\(methodName)', \(argObjectString), \(callbackParameter));
177
+ """)
178
+ } else {
179
+ CAPLog.print("Error: plugin method return type \(returnType) is not supported!")
180
+ }
181
+
182
+ // Close the function
183
+ lines.append("}")
184
+ return lines.joined(separator: "\n")
185
+ }
186
+
187
+ static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws {
188
+ if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) {
189
+ self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController)
190
+ }
191
+ }
192
+
193
+ static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) {
194
+ let fileManager = FileManager.default
195
+ do {
196
+ let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: [])
197
+ for fileURL in fileURLs {
198
+ if fileURL.hasDirectoryPath {
199
+ injectFilesForFolder(folder: fileURL, userContentController: userContentController)
200
+ } else {
201
+ try self.injectFile(fileURL: fileURL, userContentController: userContentController)
202
+ }
203
+ }
204
+ } catch {
205
+ CAPLog.print("Error while enumerating files")
206
+ }
207
+ }
208
+
209
+ static func injectFile(fileURL: URL, userContentController: WKUserContentController) throws {
210
+ do {
211
+ let data = try String(contentsOf: fileURL, encoding: .utf8)
212
+ let userScript = WKUserScript(source: data, injectionTime: .atDocumentStart, forMainFrameOnly: true)
213
+ userContentController.addUserScript(userScript)
214
+ } catch {
215
+ CAPLog.print("Unable to inject js file")
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,254 @@
1
+ import Foundation
2
+
3
+ // declare our empty protocol, and conformance, for typing
4
+ public protocol JSValue {}
5
+ extension String: JSValue {}
6
+ extension Bool: JSValue {}
7
+ extension Int: JSValue {}
8
+ extension Float: JSValue {}
9
+ extension Double: JSValue {}
10
+ extension NSNumber: JSValue {}
11
+ extension NSNull: JSValue {}
12
+ extension Array: JSValue {}
13
+ extension Date: JSValue {}
14
+ extension Dictionary: JSValue where Key == String, Value == JSValue {}
15
+
16
+ // convenience aliases
17
+ public typealias JSObject = [String: JSValue]
18
+ public typealias JSArray = [JSValue]
19
+
20
+ // string types
21
+ public protocol JSStringContainer {
22
+ func getString(_ key: String, _ defaultValue: String) -> String
23
+ func getString(_ key: String) -> String?
24
+ }
25
+
26
+ extension JSStringContainer {
27
+ public func getString(_ key: String, _ defaultValue: String) -> String {
28
+ return getString(key) ?? defaultValue
29
+ }
30
+ }
31
+
32
+ // boolean types
33
+ public protocol JSBoolContainer {
34
+ func getBool(_ key: String, _ defaultValue: Bool) -> Bool
35
+ func getBool(_ key: String) -> Bool?
36
+ }
37
+
38
+ extension JSBoolContainer {
39
+ public func getBool(_ key: String, _ defaultValue: Bool) -> Bool {
40
+ return getBool(key) ?? defaultValue
41
+ }
42
+ }
43
+
44
+ // integer types
45
+ public protocol JSIntContainer {
46
+ func getInt(_ key: String, _ defaultValue: Int) -> Int
47
+ func getInt(_ key: String) -> Int?
48
+ }
49
+
50
+ extension JSIntContainer {
51
+ public func getInt(_ key: String, _ defaultValue: Int) -> Int {
52
+ return getInt(key) ?? defaultValue
53
+ }
54
+ }
55
+
56
+ // float types
57
+ public protocol JSFloatContainer {
58
+ func getFloat(_ key: String, _ defaultValue: Float) -> Float
59
+ func getFloat(_ key: String) -> Float?
60
+ }
61
+
62
+ extension JSFloatContainer {
63
+ public func getFloat(_ key: String, _ defaultValue: Float) -> Float {
64
+ return getFloat(key) ?? defaultValue
65
+ }
66
+ }
67
+
68
+ // double types
69
+ public protocol JSDoubleContainer {
70
+ func getDouble(_ key: String, _ defaultValue: Double) -> Double
71
+ func getDouble(_ key: String) -> Double?
72
+ }
73
+
74
+ extension JSDoubleContainer {
75
+ public func getDouble(_ key: String, _ defaultValue: Double) -> Double {
76
+ return getDouble(key) ?? defaultValue
77
+ }
78
+ }
79
+
80
+ // date types
81
+ public protocol JSDateContainer {
82
+ func getDate(_ key: String, _ defaultValue: Date) -> Date
83
+ func getDate(_ key: String) -> Date?
84
+ }
85
+
86
+ extension JSDateContainer {
87
+ public func getDate(_ key: String, _ defaultValue: Date) -> Date {
88
+ return getDate(key) ?? defaultValue
89
+ }
90
+ }
91
+
92
+ // array types
93
+ public protocol JSArrayContainer {
94
+ func getArray(_ key: String, _ defaultValue: JSArray) -> JSArray
95
+ func getArray<T>(_ key: String, _ ofType: T.Type) -> [T]?
96
+ func getArray(_ key: String) -> JSArray?
97
+ }
98
+
99
+ extension JSArrayContainer {
100
+ public func getArray(_ key: String, _ defaultValue: JSArray) -> JSArray {
101
+ return getArray(key) ?? defaultValue
102
+ }
103
+
104
+ public func getArray<T>(_ key: String, _ ofType: T.Type) -> [T]? {
105
+ return getArray(key) as? [T]
106
+ }
107
+ }
108
+
109
+ // dictionary types
110
+ public protocol JSObjectContainer {
111
+ func getObject(_ key: String, _ defaultValue: JSObject) -> JSObject
112
+ func getObject(_ key: String) -> JSObject?
113
+ }
114
+
115
+ extension JSObjectContainer {
116
+ public func getObject(_ key: String, _ defaultValue: JSObject) -> JSObject {
117
+ return getObject(key) ?? defaultValue
118
+ }
119
+ }
120
+
121
+ public protocol JSValueContainer: JSStringContainer, JSBoolContainer, JSIntContainer, JSFloatContainer,
122
+ JSDoubleContainer, JSDateContainer, JSArrayContainer, JSObjectContainer {
123
+ static var jsDateFormatter: ISO8601DateFormatter { get }
124
+ var jsObjectRepresentation: JSObject { get }
125
+ }
126
+
127
+ extension JSValueContainer {
128
+ public func getValue(_ key: String) -> JSValue? {
129
+ return jsObjectRepresentation[key]
130
+ }
131
+
132
+ @available(*, message: "All values returned conform to JSValue, use getValue(_:) instead.", renamed: "getValue(_:)")
133
+ public func getAny(_ key: String) -> Any? {
134
+ return getValue(key)
135
+ }
136
+
137
+ public func getString(_ key: String) -> String? {
138
+ return jsObjectRepresentation[key] as? String
139
+ }
140
+
141
+ public func getBool(_ key: String) -> Bool? {
142
+ return jsObjectRepresentation[key] as? Bool
143
+ }
144
+
145
+ public func getInt(_ key: String) -> Int? {
146
+ return jsObjectRepresentation[key] as? Int
147
+ }
148
+
149
+ public func getFloat(_ key: String) -> Float? {
150
+ if let floatValue = jsObjectRepresentation[key] as? Float {
151
+ return floatValue
152
+ } else if let doubleValue = jsObjectRepresentation[key] as? Double {
153
+ return Float(doubleValue)
154
+ }
155
+ return nil
156
+ }
157
+
158
+ public func getDouble(_ key: String) -> Double? {
159
+ return jsObjectRepresentation[key] as? Double
160
+ }
161
+
162
+ public func getDate(_ key: String) -> Date? {
163
+ if let isoString = jsObjectRepresentation[key] as? String {
164
+ return Self.jsDateFormatter.date(from: isoString)
165
+ }
166
+ return jsObjectRepresentation[key] as? Date
167
+ }
168
+
169
+ public func getArray(_ key: String) -> JSArray? {
170
+ return jsObjectRepresentation[key] as? JSArray
171
+ }
172
+
173
+ public func getObject(_ key: String) -> JSObject? {
174
+ return jsObjectRepresentation[key] as? JSObject
175
+ }
176
+
177
+ /// Decodes a value of the given type for the given key.
178
+ /// - Parameters:
179
+ /// - type: The type of the value to decode.
180
+ /// - key: The key that the decoded value is associated with.
181
+ /// - decoder: The decoder to use to decode the value. Defaults to `JSValueDecoder()`.
182
+ /// - Returns: A value of the requested type, if present for the given key and convertible to the requested type.
183
+ /// - Throws: `DecodingError` if the encountered encoded value is corrupted.
184
+ public func decode<T: Decodable>(_ type: T.Type, for key: String, with decoder: JSValueDecoder = JSValueDecoder()) throws -> T {
185
+ try decoder.decode(type, from: jsObjectRepresentation[key] ?? [:])
186
+ }
187
+ }
188
+
189
+ @objc protocol BridgedJSValueContainer: NSObjectProtocol {
190
+ static var jsDateFormatter: ISO8601DateFormatter { get }
191
+ var dictionaryRepresentation: NSDictionary { get }
192
+ }
193
+
194
+ /*
195
+ Simply casting objects from foundation class clusters (such as __NSArrayM)
196
+ doesn't work with the JSValue protocol and will always fail. So we need to
197
+ recursively and explicitly convert each value in the dictionary.
198
+ */
199
+ public enum JSTypes {}
200
+ extension JSTypes {
201
+ public static func coerceDictionaryToJSObject(_ dictionary: NSDictionary?, formattingDatesAsStrings: Bool = false) -> JSObject? {
202
+ return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject
203
+ }
204
+
205
+ public static func coerceDictionaryToJSObject(_ dictionary: [AnyHashable: Any]?, formattingDatesAsStrings: Bool = false) -> JSObject? {
206
+ return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject
207
+ }
208
+
209
+ public static func coerceArrayToJSArray(_ array: [Any]?, formattingDatesAsStrings: Bool = false) -> JSArray? {
210
+ return array?.compactMap { coerceToJSValue($0, formattingDates: formattingDatesAsStrings) }
211
+ }
212
+ }
213
+
214
+ private let dateStringFormatter = ISO8601DateFormatter()
215
+
216
+ // We need a large switch statement because we have a lot of types.
217
+ // swiftlint:disable:next cyclomatic_complexity
218
+ private func coerceToJSValue(_ value: Any?, formattingDates: Bool) -> JSValue? {
219
+ guard let value = value else {
220
+ return nil
221
+ }
222
+ switch value {
223
+ case let stringValue as String:
224
+ return stringValue
225
+ case let numberValue as NSNumber:
226
+ return numberValue
227
+ case let boolValue as Bool:
228
+ return boolValue
229
+ case let intValue as Int:
230
+ return intValue
231
+ case let floatValue as Float:
232
+ return floatValue
233
+ case let doubleValue as Double:
234
+ return doubleValue
235
+ case let dateValue as Date:
236
+ if formattingDates {
237
+ return dateStringFormatter.string(from: dateValue)
238
+ }
239
+ return dateValue
240
+ case let nullValue as NSNull:
241
+ return nullValue
242
+ case let arrayValue as NSArray:
243
+ return arrayValue.compactMap { coerceToJSValue($0, formattingDates: formattingDates) }
244
+ case let dictionaryValue as NSDictionary:
245
+ let keys = dictionaryValue.allKeys.compactMap { $0 as? String }
246
+ var result: JSObject = [:]
247
+ for key in keys {
248
+ result[key] = coerceToJSValue(dictionaryValue[key], formattingDates: formattingDates)
249
+ }
250
+ return result
251
+ default:
252
+ return nil
253
+ }
254
+ }
@@ -0,0 +1,60 @@
1
+ import Foundation
2
+
3
+ public struct KeyPath {
4
+ var segments: [String]
5
+ var isEmpty: Bool { return segments.isEmpty }
6
+ var path: String {
7
+ return segments.joined(separator: ".")
8
+ }
9
+
10
+ // initializers
11
+ init(_ string: String) {
12
+ self.segments = string.components(separatedBy: ".")
13
+ }
14
+
15
+ init(segments: [String]) {
16
+ self.segments = segments
17
+ }
18
+
19
+ // returns a tuple of the first segment and the remaining key path. result is nil if the key path has no segments.
20
+ func headAndRemainder() -> (head: String, remainder: KeyPath)? {
21
+ guard !isEmpty else {
22
+ return nil
23
+ }
24
+ var paths = segments
25
+ let head = paths.removeFirst()
26
+ return (head, KeyPath(segments: paths))
27
+ }
28
+ }
29
+
30
+ extension KeyPath: ExpressibleByStringLiteral {
31
+ public init(stringLiteral value: String) {
32
+ self.init(value)
33
+ }
34
+
35
+ public init(unicodeScalarLiteral value: String) {
36
+ self.init(value)
37
+ }
38
+
39
+ public init(extendedGraphemeClusterLiteral value: String) {
40
+ self.init(value)
41
+ }
42
+ }
43
+
44
+ extension JSObject {
45
+ public subscript(keyPath keyPath: KeyPath) -> JSValue? {
46
+ switch keyPath.headAndRemainder() {
47
+ case nil: // path is empty
48
+ return nil
49
+ case let (head, remainder)? where remainder.isEmpty: // reached the end of the path
50
+ return self[head]
51
+ case let (head, remainder)?: // we have at least one level to traverse
52
+ switch self[head] {
53
+ case let childObject as JSObject: // iterate down the next level
54
+ return childObject[keyPath: remainder]
55
+ default: // not an object, can't go any deeper
56
+ return nil
57
+ }
58
+ }
59
+ }
60
+ }