@capacitor/ios 6.0.0-alpha.2 → 6.0.0-beta.1
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.
- package/Capacitor/Capacitor/CAPInstanceDescriptor.swift +1 -1
- package/Capacitor/Capacitor/CAPPluginCall.swift +26 -0
- package/Capacitor/Capacitor/Codable/JSValueDecoder.swift +320 -0
- package/Capacitor/Capacitor/Codable/JSValueEncoder.swift +541 -0
- package/Capacitor/Capacitor/JSTypes.swift +11 -0
- package/Capacitor/Capacitor/assets/native-bridge.js +47 -3
- package/Capacitor.podspec +2 -2
- package/CapacitorCordova/CapacitorCordova/CapacitorCordova.h +1 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h +1 -1
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.h +27 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.m +49 -0
- package/package.json +2 -2
|
@@ -33,7 +33,7 @@ internal extension InstanceDescriptor {
|
|
|
33
33
|
// sanity check that the app directory is valid
|
|
34
34
|
var isDirectory: ObjCBool = ObjCBool(false)
|
|
35
35
|
if warnings.contains(.missingAppDir) == false,
|
|
36
|
-
|
|
36
|
+
FileManager.default.fileExists(atPath: appLocation.path, isDirectory: &isDirectory) == false || isDirectory.boolValue == false {
|
|
37
37
|
warnings.update(with: .missingAppDir)
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -77,3 +77,29 @@ extension CAPPluginCall: JSValueContainer {
|
|
|
77
77
|
errorHandler(CAPPluginCallError(message: message, code: "UNAVAILABLE", error: nil, data: [:]))
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
// MARK: Codable Support
|
|
82
|
+
public extension CAPPluginCall {
|
|
83
|
+
/// Encodes the given value to a ``JSObject`` and resolves the call.
|
|
84
|
+
/// - Parameters:
|
|
85
|
+
/// - data: The value to encode.
|
|
86
|
+
/// - encoder: The encoder to use. Defaults to `JSValueEncoder()`.
|
|
87
|
+
/// - Throws: If the value cannot be encoded.
|
|
88
|
+
///
|
|
89
|
+
/// The data paramater _must_ be encodable as a
|
|
90
|
+
/// ``JSObject``, otherwise this method will throw.
|
|
91
|
+
func resolve<T: Encodable>(with data: T, encoder: JSValueEncoder = JSValueEncoder()) throws {
|
|
92
|
+
let encoded = try encoder.encodeJSObject(data)
|
|
93
|
+
resolve(encoded)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// Decodes the options to the given type.
|
|
97
|
+
/// - Parameters:
|
|
98
|
+
/// - type: The type to decode to.
|
|
99
|
+
/// - decoder: The decoder to use. Defaults to `JSValueDecoder()`.
|
|
100
|
+
/// - Throws: If the options cannot be decoded.
|
|
101
|
+
/// - Returns: The decoded value.
|
|
102
|
+
func decode<T: Decodable>(_ type: T.Type, decoder: JSValueDecoder = JSValueDecoder()) throws -> T {
|
|
103
|
+
try decoder.decode(type, from: options as? JSObject ?? [:])
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
//
|
|
2
|
+
// JSValueDecoder.swift
|
|
3
|
+
// Capacitor
|
|
4
|
+
//
|
|
5
|
+
// Created by Steven Sherry on 12/8/23.
|
|
6
|
+
// Copyright © 2023 Drifty Co. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
import Combine
|
|
11
|
+
|
|
12
|
+
/// A decoder that can decode ``JSValue`` objects into `Decodable` types.
|
|
13
|
+
public final class JSValueDecoder: TopLevelDecoder {
|
|
14
|
+
public init() {}
|
|
15
|
+
|
|
16
|
+
/// Decodes a ``JSValue`` into the provided `Decodable` type
|
|
17
|
+
/// - Parameters:
|
|
18
|
+
/// - type: The type of the value to decode from the provided ``JSValue`` object
|
|
19
|
+
/// - data: The ``JSValue`` to decode
|
|
20
|
+
/// - Returns: A value of the specified type.
|
|
21
|
+
///
|
|
22
|
+
/// An error will be thrown from this method for two possible reasons:
|
|
23
|
+
/// 1. A type mismatch was found.
|
|
24
|
+
/// 2. A key was not found in the `data` field that is required in the `type` provided.
|
|
25
|
+
public func decode<T>(_ type: T.Type, from data: JSValue) throws -> T where T: Decodable {
|
|
26
|
+
let decoder = _JSValueDecoder(data: data)
|
|
27
|
+
return try T(from: decoder)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
typealias CodingUserInfo = [CodingUserInfoKey: Any]
|
|
32
|
+
|
|
33
|
+
private final class _JSValueDecoder {
|
|
34
|
+
internal var codingPath: [CodingKey] = []
|
|
35
|
+
internal var userInfo: CodingUserInfo = [:]
|
|
36
|
+
fileprivate var data: JSValue
|
|
37
|
+
|
|
38
|
+
init(data: JSValue) {
|
|
39
|
+
self.data = data
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
extension _JSValueDecoder: Decoder {
|
|
44
|
+
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {
|
|
45
|
+
guard let data = data as? JSObject else {
|
|
46
|
+
throw DecodingError.typeMismatch(JSObject.self, on: data, codingPath: codingPath)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return KeyedDecodingContainer(
|
|
50
|
+
KeyedContainer(
|
|
51
|
+
data: data,
|
|
52
|
+
codingPath: codingPath,
|
|
53
|
+
userInfo: userInfo
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
|
|
59
|
+
guard let data = data as? JSArray else {
|
|
60
|
+
throw DecodingError.typeMismatch(JSArray.self, on: data, codingPath: codingPath)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return UnkeyedContainer(data: data, codingPath: codingPath, userInfo: userInfo)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func singleValueContainer() throws -> SingleValueDecodingContainer {
|
|
67
|
+
SingleValueContainer(data: data, codingPath: codingPath, userInfo: userInfo)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private final class KeyedContainer<Key> where Key: CodingKey {
|
|
72
|
+
var data: JSObject
|
|
73
|
+
var codingPath: [CodingKey]
|
|
74
|
+
var userInfo: CodingUserInfo
|
|
75
|
+
var allKeys: [Key]
|
|
76
|
+
|
|
77
|
+
init(data: JSObject, codingPath: [CodingKey], userInfo: CodingUserInfo) {
|
|
78
|
+
self.data = data
|
|
79
|
+
self.codingPath = codingPath
|
|
80
|
+
self.userInfo = userInfo
|
|
81
|
+
self.allKeys = data.keys.compactMap(Key.init(stringValue:))
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
extension KeyedContainer: KeyedDecodingContainerProtocol {
|
|
86
|
+
func contains(_ key: Key) -> Bool {
|
|
87
|
+
allKeys.contains { $0.stringValue == key.stringValue }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func decodeNil(forKey key: Key) throws -> Bool {
|
|
91
|
+
data[key.stringValue] == nil || data[key.stringValue] is NSNull
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Decodable {
|
|
95
|
+
guard let rawValue = data[key.stringValue] else {
|
|
96
|
+
throw DecodingError.keyNotFound(key, on: data, codingPath: codingPath)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var newPath = codingPath
|
|
100
|
+
newPath.append(key)
|
|
101
|
+
let decoder = _JSValueDecoder(data: rawValue)
|
|
102
|
+
return try T(from: decoder)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
|
|
106
|
+
var newPath = codingPath
|
|
107
|
+
newPath.append(key)
|
|
108
|
+
guard let data = data[key.stringValue] as? JSArray else {
|
|
109
|
+
throw DecodingError.typeMismatch(
|
|
110
|
+
JSArray.self,
|
|
111
|
+
on: data[key.stringValue] ?? "null value",
|
|
112
|
+
codingPath: newPath
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return UnkeyedContainer(data: data, codingPath: newPath, userInfo: userInfo)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
|
|
120
|
+
var newPath = codingPath
|
|
121
|
+
newPath.append(key)
|
|
122
|
+
guard let data = data[key.stringValue] as? JSObject else {
|
|
123
|
+
throw DecodingError.typeMismatch(
|
|
124
|
+
JSObject.self,
|
|
125
|
+
on: data[key.stringValue] ?? "null value",
|
|
126
|
+
codingPath: newPath
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return KeyedDecodingContainer(KeyedContainer<NestedKey>(data: data, codingPath: newPath, userInfo: userInfo))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
enum SuperKey: String, CodingKey { case `super` }
|
|
134
|
+
|
|
135
|
+
func superDecoder() throws -> Decoder {
|
|
136
|
+
var newPath = codingPath
|
|
137
|
+
newPath.append(SuperKey.super)
|
|
138
|
+
guard let data = data[SuperKey.super.stringValue] else {
|
|
139
|
+
throw DecodingError.keyNotFound(SuperKey.super, on: data, codingPath: newPath)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return _JSValueDecoder(data: data)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func superDecoder(forKey key: Key) throws -> Decoder {
|
|
146
|
+
var newPath = codingPath
|
|
147
|
+
newPath.append(key)
|
|
148
|
+
guard let data = data[key.stringValue] else {
|
|
149
|
+
throw DecodingError.keyNotFound(key, on: data, codingPath: newPath)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return _JSValueDecoder(data: data)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private final class UnkeyedContainer {
|
|
157
|
+
var data: JSArray
|
|
158
|
+
var codingPath: [CodingKey]
|
|
159
|
+
var userInfo: CodingUserInfo
|
|
160
|
+
private(set) var currentIndex = 0
|
|
161
|
+
|
|
162
|
+
init(data: JSArray, codingPath: [CodingKey], userInfo: CodingUserInfo) {
|
|
163
|
+
self.data = data
|
|
164
|
+
self.codingPath = codingPath
|
|
165
|
+
self.userInfo = userInfo
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
extension UnkeyedContainer: UnkeyedDecodingContainer {
|
|
170
|
+
var count: Int? {
|
|
171
|
+
data.count
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
var isAtEnd: Bool {
|
|
175
|
+
currentIndex == data.endIndex
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func decodeNil() throws -> Bool {
|
|
179
|
+
defer { currentIndex += 1 }
|
|
180
|
+
return data[currentIndex] is NSNull
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {
|
|
184
|
+
defer { currentIndex += 1 }
|
|
185
|
+
let decoder = _JSValueDecoder(data: data[currentIndex])
|
|
186
|
+
return try T(from: decoder)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
|
190
|
+
defer { currentIndex += 1 }
|
|
191
|
+
guard let data = data[currentIndex] as? JSArray else {
|
|
192
|
+
throw DecodingError.typeMismatch(JSArray.self, on: data[currentIndex], codingPath: codingPath)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return UnkeyedContainer(data: data, codingPath: codingPath, userInfo: userInfo)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
|
|
199
|
+
defer { currentIndex += 1 }
|
|
200
|
+
guard let data = data[currentIndex] as? JSObject else {
|
|
201
|
+
throw DecodingError.typeMismatch(JSObject.self, on: data[currentIndex], codingPath: codingPath)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return KeyedDecodingContainer(KeyedContainer(data: data, codingPath: codingPath, userInfo: userInfo))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
func superDecoder() throws -> Decoder {
|
|
208
|
+
defer { currentIndex += 1 }
|
|
209
|
+
let data = data[currentIndex]
|
|
210
|
+
return _JSValueDecoder(data: data)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private final class SingleValueContainer {
|
|
215
|
+
var data: JSValue
|
|
216
|
+
var codingPath: [CodingKey]
|
|
217
|
+
var userInfo: CodingUserInfo
|
|
218
|
+
|
|
219
|
+
init(data: JSValue, codingPath: [CodingKey], userInfo: CodingUserInfo) {
|
|
220
|
+
self.data = data
|
|
221
|
+
self.codingPath = codingPath
|
|
222
|
+
self.userInfo = userInfo
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
extension SingleValueContainer: SingleValueDecodingContainer {
|
|
227
|
+
func decodeNil() -> Bool {
|
|
228
|
+
return data is NSNull
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private func cast<T>(to type: T.Type) throws -> T {
|
|
232
|
+
guard let data = data as? T else {
|
|
233
|
+
throw DecodingError.typeMismatch(type, on: data, codingPath: codingPath)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return data
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
func decode(_ type: Bool.Type) throws -> Bool {
|
|
240
|
+
try cast(to: type)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
func decode(_ type: String.Type) throws -> String {
|
|
244
|
+
try cast(to: type)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
func decode(_ type: Double.Type) throws -> Double {
|
|
248
|
+
try cast(to: type)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func decode(_ type: Float.Type) throws -> Float {
|
|
252
|
+
try cast(to: type)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
func decode(_ type: Int.Type) throws -> Int {
|
|
256
|
+
try cast(to: type)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
func decode(_ type: Int8.Type) throws -> Int8 {
|
|
260
|
+
try cast(to: type)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
func decode(_ type: Int16.Type) throws -> Int16 {
|
|
264
|
+
try cast(to: type)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
func decode(_ type: Int32.Type) throws -> Int32 {
|
|
268
|
+
try cast(to: type)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
func decode(_ type: Int64.Type) throws -> Int64 {
|
|
272
|
+
try cast(to: type)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
func decode(_ type: UInt.Type) throws -> UInt {
|
|
276
|
+
try cast(to: type)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
func decode(_ type: UInt8.Type) throws -> UInt8 {
|
|
280
|
+
try cast(to: type)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
func decode(_ type: UInt16.Type) throws -> UInt16 {
|
|
284
|
+
try cast(to: type)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
func decode(_ type: UInt32.Type) throws -> UInt32 {
|
|
288
|
+
try cast(to: type)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
func decode(_ type: UInt64.Type) throws -> UInt64 {
|
|
292
|
+
try cast(to: type)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {
|
|
296
|
+
let decoder = _JSValueDecoder(data: data)
|
|
297
|
+
return try T(from: decoder)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
extension DecodingError {
|
|
302
|
+
static func typeMismatch(_ type: Any.Type, on data: JSValue, codingPath: [CodingKey]) -> DecodingError {
|
|
303
|
+
return .typeMismatch(
|
|
304
|
+
type,
|
|
305
|
+
.init(
|
|
306
|
+
codingPath: codingPath,
|
|
307
|
+
debugDescription: "\(data) was unable to be cast to \(type)."
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static func keyNotFound(_ key: any CodingKey, on data: JSValue, codingPath: [CodingKey]) -> DecodingError {
|
|
313
|
+
return .keyNotFound(
|
|
314
|
+
key,
|
|
315
|
+
.init(
|
|
316
|
+
codingPath: codingPath,
|
|
317
|
+
debugDescription: "Key \(key.stringValue) not found in \(data)")
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
//
|
|
2
|
+
// JSValueEncoder.swift
|
|
3
|
+
// Capacitor
|
|
4
|
+
//
|
|
5
|
+
// Created by Steven Sherry on 12/8/23.
|
|
6
|
+
// Copyright © 2023 Drifty Co. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
import Combine
|
|
11
|
+
|
|
12
|
+
/// An encoder than can encode ``JSValue`` objects from `Encodable` types
|
|
13
|
+
public final class JSValueEncoder: TopLevelEncoder {
|
|
14
|
+
/// The strategy to use when encoding `nil` values
|
|
15
|
+
public enum OptionalEncodingStrategy {
|
|
16
|
+
/// Encode `nil` values as `NSNull`
|
|
17
|
+
case explicitNulls
|
|
18
|
+
/// Excludes the value from the encoded object altogether
|
|
19
|
+
case undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// The strategy to use when encoding `nil` values
|
|
23
|
+
public var optionalEncodingStrategy: OptionalEncodingStrategy
|
|
24
|
+
|
|
25
|
+
/// Creates a new `JSValueEncoder`
|
|
26
|
+
/// - Parameter optionalEncodingStrategy: The strategy to use when encoding `nil` values
|
|
27
|
+
public init(optionalEncodingStrategy: OptionalEncodingStrategy = .undefined) {
|
|
28
|
+
self.optionalEncodingStrategy = optionalEncodingStrategy
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Encodes an `Encodable` value to a ``JSValue``
|
|
32
|
+
/// - Parameter value: The value to encode to ``JSValue``
|
|
33
|
+
/// - Returns: The encoded ``JSValue``
|
|
34
|
+
/// - Throws: An error if the value could not be encoded as a ``JSValue``
|
|
35
|
+
public func encode<T>(_ value: T) throws -> JSValue where T: Encodable {
|
|
36
|
+
let encoder = _JSValueEncoder(optionalEncodingStrategy: optionalEncodingStrategy)
|
|
37
|
+
try value.encode(to: encoder)
|
|
38
|
+
guard let value = encoder.data else {
|
|
39
|
+
throw EncodingError.invalidValue(
|
|
40
|
+
value,
|
|
41
|
+
.init(
|
|
42
|
+
codingPath: encoder.codingPath,
|
|
43
|
+
debugDescription: "\(value) was unable to be encoded as a JSValue"
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return value
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Encodes an `Encodable` value to a ``JSObject``
|
|
52
|
+
/// - Parameter value: The value to encode to a ``JSObject``
|
|
53
|
+
/// - Returns: The encoded ``JSObject``
|
|
54
|
+
/// - Throws: An error if the value could not be encoded as a ``JSObject``
|
|
55
|
+
///
|
|
56
|
+
/// This method is a convenience method for encoding an `Encodable` value to a ``JSObject``.
|
|
57
|
+
/// It is equivalent to calling ``encode(_:)`` and casting the result to a ``JSObject`` and
|
|
58
|
+
/// throwing an error if the cast fails.
|
|
59
|
+
public func encodeJSObject<T>(_ value: T) throws -> JSObject where T: Encodable {
|
|
60
|
+
guard let object = try encode(value) as? JSObject else {
|
|
61
|
+
throw EncodingError.invalidValue(
|
|
62
|
+
value,
|
|
63
|
+
.init(
|
|
64
|
+
codingPath: [],
|
|
65
|
+
debugDescription: "\(value) was unable to be encoded as a JSObject"
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return object
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private protocol JSValueEncodingContainer {
|
|
75
|
+
var data: JSValue? { get }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private enum EncodingContainer: JSValueEncodingContainer {
|
|
79
|
+
case singleValue(SingleValueContainer)
|
|
80
|
+
case unkeyed(UnkeyedContainer)
|
|
81
|
+
case keyed(AnyKeyedContainer)
|
|
82
|
+
|
|
83
|
+
var data: JSValue? {
|
|
84
|
+
switch self {
|
|
85
|
+
case let .singleValue(container):
|
|
86
|
+
return container.data
|
|
87
|
+
case let .unkeyed(container):
|
|
88
|
+
return container.data
|
|
89
|
+
case let .keyed(container):
|
|
90
|
+
return container.data
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
var type: String {
|
|
95
|
+
switch self {
|
|
96
|
+
case .singleValue:
|
|
97
|
+
"SingleValueContainer"
|
|
98
|
+
case .unkeyed:
|
|
99
|
+
"UnkeyedContainer"
|
|
100
|
+
case .keyed:
|
|
101
|
+
"KeyedContainer"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private final class _JSValueEncoder: JSValueEncodingContainer {
|
|
107
|
+
var codingPath: [CodingKey] = []
|
|
108
|
+
var data: JSValue? {
|
|
109
|
+
containers.data
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy
|
|
113
|
+
|
|
114
|
+
var userInfo: CodingUserInfo = [:]
|
|
115
|
+
fileprivate var containers: [EncodingContainer] = []
|
|
116
|
+
|
|
117
|
+
init(optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy) {
|
|
118
|
+
self.optionalEncodingStrategy = optionalEncodingStrategy
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
extension Array: JSValueEncodingContainer where Element == EncodingContainer {
|
|
123
|
+
var data: JSValue? {
|
|
124
|
+
guard count != 0 else { return nil }
|
|
125
|
+
guard count != 1 else { return self[0].data }
|
|
126
|
+
var data: (any JSValue)? = nil
|
|
127
|
+
|
|
128
|
+
for container in self {
|
|
129
|
+
if data == nil {
|
|
130
|
+
data = container.data
|
|
131
|
+
} else {
|
|
132
|
+
// The top-level container is
|
|
133
|
+
switch container {
|
|
134
|
+
case let .keyed(container):
|
|
135
|
+
guard let obj = data as? JSObject else { break }
|
|
136
|
+
data = obj.merging(container.object() ?? [:]) { $1 }
|
|
137
|
+
case let .unkeyed(container):
|
|
138
|
+
guard var copy = data as? JSArray else { break }
|
|
139
|
+
copy.append(contentsOf: container.array ?? [])
|
|
140
|
+
data = copy
|
|
141
|
+
default:
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return data
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private enum EncodedValue {
|
|
152
|
+
case value(any JSValue)
|
|
153
|
+
case nestedContainer(any JSValueEncodingContainer)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
extension _JSValueEncoder: Encoder {
|
|
157
|
+
func addContainer(_ container: EncodingContainer) {
|
|
158
|
+
guard !containers.isEmpty else {
|
|
159
|
+
containers.append(container)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for existingContainer in containers {
|
|
164
|
+
switch (existingContainer, container) {
|
|
165
|
+
case (.unkeyed, .unkeyed), (.keyed, .keyed):
|
|
166
|
+
containers.append(container)
|
|
167
|
+
default:
|
|
168
|
+
preconditionFailure("Sibling top-level containers must be of the same type. Attempted to add a \(container)")
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
|
|
174
|
+
let container = KeyedContainer<Key>(codingPath: codingPath, userInfo: userInfo, optionalEncodingStrategy: optionalEncodingStrategy)
|
|
175
|
+
addContainer(.keyed(.init(container)))
|
|
176
|
+
return KeyedEncodingContainer(container)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
func unkeyedContainer() -> UnkeyedEncodingContainer {
|
|
180
|
+
let container = UnkeyedContainer(codingPath: codingPath, userInfo: userInfo, optionalEncodingStrategy: optionalEncodingStrategy)
|
|
181
|
+
addContainer(.unkeyed(container))
|
|
182
|
+
return container
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
func singleValueContainer() -> SingleValueEncodingContainer {
|
|
186
|
+
let container = SingleValueContainer(codingPath: codingPath, userInfo: userInfo, optionalEncodingStrategy: optionalEncodingStrategy)
|
|
187
|
+
addContainer(.singleValue(container))
|
|
188
|
+
return container
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fileprivate final class KeyedContainer<Key> where Key: CodingKey {
|
|
193
|
+
var object: JSObject? {
|
|
194
|
+
encodedKeyedValue?.reduce(into: [:]) { obj, next in
|
|
195
|
+
let (key, value) = next
|
|
196
|
+
switch value {
|
|
197
|
+
case .value(let value):
|
|
198
|
+
obj[key] = value
|
|
199
|
+
case .nestedContainer(let container):
|
|
200
|
+
obj[key] = container.data
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
var codingPath: [CodingKey]
|
|
206
|
+
var userInfo: CodingUserInfo
|
|
207
|
+
var optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy
|
|
208
|
+
private var encodedKeyedValue: [String: EncodedValue]?
|
|
209
|
+
|
|
210
|
+
init(codingPath: [CodingKey], userInfo: CodingUserInfo, optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy) {
|
|
211
|
+
self.codingPath = codingPath
|
|
212
|
+
self.userInfo = userInfo
|
|
213
|
+
self.optionalEncodingStrategy = optionalEncodingStrategy
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
extension KeyedContainer: KeyedEncodingContainerProtocol {
|
|
218
|
+
func insert(_ value: JSValue, for key: Key) {
|
|
219
|
+
insert(.value(value), for: key)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
func insert<K: CodingKey>(_ encodedValue: EncodedValue, for key: K) {
|
|
223
|
+
if encodedKeyedValue == nil {
|
|
224
|
+
encodedKeyedValue = [key.stringValue: encodedValue]
|
|
225
|
+
} else {
|
|
226
|
+
encodedKeyedValue![key.stringValue] = encodedValue
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
func encodeNil(forKey key: Key) throws {
|
|
231
|
+
insert(NSNull(), for: key)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
func encode<T>(_ value: T, forKey key: Key) throws where T: Encodable {
|
|
235
|
+
let encoder = _JSValueEncoder(optionalEncodingStrategy: optionalEncodingStrategy)
|
|
236
|
+
try value.encode(to: encoder)
|
|
237
|
+
insert(.nestedContainer(encoder), for: key)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// This is a perectly valid name for this method. The underscore is to avoid a conflict with the
|
|
241
|
+
// protocol requirement.
|
|
242
|
+
//swiftlint:disable:next identifier_name
|
|
243
|
+
func _encodeIfPresent<T>(_ value: T?, forKey key: Key) throws where T: Encodable {
|
|
244
|
+
switch optionalEncodingStrategy {
|
|
245
|
+
case .explicitNulls:
|
|
246
|
+
if let value = value {
|
|
247
|
+
try encode(value, forKey: key)
|
|
248
|
+
} else {
|
|
249
|
+
try encodeNil(forKey: key)
|
|
250
|
+
}
|
|
251
|
+
case .undefined:
|
|
252
|
+
guard let value = value else { return }
|
|
253
|
+
try encode(value, forKey: key)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
func encodeIfPresent<T>(_ value: T?, forKey key: Key) throws where T: Encodable {
|
|
258
|
+
try _encodeIfPresent(value, forKey: key)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {
|
|
262
|
+
try _encodeIfPresent(value, forKey: key)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
func encodeIfPresent(_ value: String?, forKey key: Key) throws {
|
|
266
|
+
try _encodeIfPresent(value, forKey: key)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func encodeIfPresent(_ value: Double?, forKey key: Key) throws {
|
|
270
|
+
try _encodeIfPresent(value, forKey: key)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
|
|
274
|
+
try _encodeIfPresent(value, forKey: key)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
func encodeIfPresent(_ value: Int?, forKey key: Key) throws {
|
|
278
|
+
try _encodeIfPresent(value, forKey: key)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
func encodeIfPresent(_ value: Int8?, forKey key: Key) throws {
|
|
282
|
+
try _encodeIfPresent(value, forKey: key)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
func encodeIfPresent(_ value: Int16?, forKey key: Key) throws {
|
|
286
|
+
try _encodeIfPresent(value, forKey: key)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
func encodeIfPresent(_ value: Int32?, forKey key: Key) throws {
|
|
290
|
+
try _encodeIfPresent(value, forKey: key)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
func encodeIfPresent(_ value: Int64?, forKey key: Key) throws {
|
|
294
|
+
try _encodeIfPresent(value, forKey: key)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
func encodeIfPresent(_ value: UInt?, forKey key: Key) throws {
|
|
298
|
+
try _encodeIfPresent(value, forKey: key)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws {
|
|
302
|
+
try _encodeIfPresent(value, forKey: key)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws {
|
|
306
|
+
try _encodeIfPresent(value, forKey: key)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws {
|
|
310
|
+
try _encodeIfPresent(value, forKey: key)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws {
|
|
314
|
+
try _encodeIfPresent(value, forKey: key)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
|
|
318
|
+
var newPath = codingPath
|
|
319
|
+
newPath.append(key)
|
|
320
|
+
|
|
321
|
+
let nestedContainer = KeyedContainer<NestedKey>(
|
|
322
|
+
codingPath: newPath,
|
|
323
|
+
userInfo: userInfo,
|
|
324
|
+
optionalEncodingStrategy: optionalEncodingStrategy
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
insert(.nestedContainer(nestedContainer), for: key)
|
|
328
|
+
return KeyedEncodingContainer(nestedContainer)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
|
332
|
+
var newPath = codingPath
|
|
333
|
+
newPath.append(key)
|
|
334
|
+
let nestedContainer = UnkeyedContainer(
|
|
335
|
+
codingPath: codingPath,
|
|
336
|
+
userInfo: userInfo,
|
|
337
|
+
optionalEncodingStrategy: optionalEncodingStrategy
|
|
338
|
+
)
|
|
339
|
+
insert(.nestedContainer(nestedContainer), for: key)
|
|
340
|
+
return nestedContainer
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
enum SuperKey: String, CodingKey {
|
|
344
|
+
case `super`
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
func superEncoder() -> Encoder {
|
|
348
|
+
let encoder = _JSValueEncoder(optionalEncodingStrategy: optionalEncodingStrategy)
|
|
349
|
+
insert(.nestedContainer(encoder), for: SuperKey.super)
|
|
350
|
+
return encoder
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
func superEncoder(forKey key: Key) -> Encoder {
|
|
354
|
+
let encoder = _JSValueEncoder(optionalEncodingStrategy: optionalEncodingStrategy)
|
|
355
|
+
insert(.nestedContainer(encoder), for: key)
|
|
356
|
+
return encoder
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private class AnyKeyedContainer: JSValueEncodingContainer {
|
|
361
|
+
var data: JSValue? { object() }
|
|
362
|
+
var object: () -> JSObject?
|
|
363
|
+
|
|
364
|
+
init<Key>(_ keyedContainer: KeyedContainer<Key>) where Key: CodingKey {
|
|
365
|
+
object = { keyedContainer.object }
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
extension KeyedContainer: JSValueEncodingContainer {
|
|
370
|
+
var data: JSValue? { object }
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private final class UnkeyedContainer {
|
|
374
|
+
var array: JSArray? {
|
|
375
|
+
encodedUnkeyedValue?.reduce(into: []) { arr, next in
|
|
376
|
+
switch next {
|
|
377
|
+
case .value(let value):
|
|
378
|
+
arr.append(value)
|
|
379
|
+
case .nestedContainer(let container):
|
|
380
|
+
guard let data = container.data else { return }
|
|
381
|
+
arr.append(data)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
var codingPath: [CodingKey]
|
|
387
|
+
var userInfo: CodingUserInfo
|
|
388
|
+
var optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy
|
|
389
|
+
private var encodedUnkeyedValue: [EncodedValue]?
|
|
390
|
+
|
|
391
|
+
init(codingPath: [CodingKey], userInfo: CodingUserInfo, optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy) {
|
|
392
|
+
self.codingPath = codingPath
|
|
393
|
+
self.userInfo = userInfo
|
|
394
|
+
self.optionalEncodingStrategy = optionalEncodingStrategy
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
extension UnkeyedContainer: UnkeyedEncodingContainer {
|
|
399
|
+
private func append(_ value: any JSValue) {
|
|
400
|
+
append(.value(value))
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private func append(_ value: EncodedValue) {
|
|
404
|
+
if encodedUnkeyedValue == nil {
|
|
405
|
+
encodedUnkeyedValue = [value]
|
|
406
|
+
} else {
|
|
407
|
+
encodedUnkeyedValue!.append(value)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
var count: Int {
|
|
412
|
+
array?.count ?? 0
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
func encodeNil() throws {
|
|
416
|
+
append(NSNull())
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
func encode<T>(_ value: T) throws where T: Encodable {
|
|
420
|
+
let encoder = _JSValueEncoder(optionalEncodingStrategy: optionalEncodingStrategy)
|
|
421
|
+
try value.encode(to: encoder)
|
|
422
|
+
append(.nestedContainer(encoder))
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
|
426
|
+
let nestedContainer = UnkeyedContainer(
|
|
427
|
+
codingPath: codingPath,
|
|
428
|
+
userInfo: userInfo,
|
|
429
|
+
optionalEncodingStrategy: optionalEncodingStrategy
|
|
430
|
+
)
|
|
431
|
+
append(.nestedContainer(nestedContainer))
|
|
432
|
+
return nestedContainer
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
|
|
436
|
+
let nestedContainer = KeyedContainer<NestedKey>(
|
|
437
|
+
codingPath: codingPath,
|
|
438
|
+
userInfo: userInfo,
|
|
439
|
+
optionalEncodingStrategy: optionalEncodingStrategy
|
|
440
|
+
)
|
|
441
|
+
append(.nestedContainer(nestedContainer))
|
|
442
|
+
return KeyedEncodingContainer(nestedContainer)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
func superEncoder() -> Encoder {
|
|
446
|
+
let encoder = _JSValueEncoder(optionalEncodingStrategy: optionalEncodingStrategy)
|
|
447
|
+
append(.nestedContainer(encoder))
|
|
448
|
+
return encoder
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
extension UnkeyedContainer: JSValueEncodingContainer {
|
|
453
|
+
var data: JSValue? { array }
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
private final class SingleValueContainer {
|
|
457
|
+
var data: JSValue?
|
|
458
|
+
var codingPath: [CodingKey]
|
|
459
|
+
var userInfo: CodingUserInfo
|
|
460
|
+
var optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy
|
|
461
|
+
|
|
462
|
+
init(
|
|
463
|
+
codingPath: [CodingKey],
|
|
464
|
+
userInfo: CodingUserInfo,
|
|
465
|
+
optionalEncodingStrategy: JSValueEncoder.OptionalEncodingStrategy
|
|
466
|
+
) {
|
|
467
|
+
self.codingPath = codingPath
|
|
468
|
+
self.userInfo = userInfo
|
|
469
|
+
self.optionalEncodingStrategy = optionalEncodingStrategy
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
extension SingleValueContainer: SingleValueEncodingContainer {
|
|
474
|
+
func encodeNil() throws {
|
|
475
|
+
data = NSNull()
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
func encode(_ value: Bool) throws {
|
|
479
|
+
data = value
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
func encode(_ value: String) throws {
|
|
483
|
+
data = value
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
func encode(_ value: Double) throws {
|
|
487
|
+
data = value as NSNumber
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
func encode(_ value: Float) throws {
|
|
491
|
+
data = value as NSNumber
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
func encode(_ value: Int) throws {
|
|
495
|
+
data = value as NSNumber
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
func encode(_ value: Int8) throws {
|
|
499
|
+
data = value as NSNumber
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
func encode(_ value: Int16) throws {
|
|
503
|
+
data = value as NSNumber
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
func encode(_ value: Int32) throws {
|
|
507
|
+
data = value as NSNumber
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
func encode(_ value: Int64) throws {
|
|
511
|
+
data = value as NSNumber
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
func encode(_ value: UInt) throws {
|
|
515
|
+
data = value as NSNumber
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
func encode(_ value: UInt8) throws {
|
|
519
|
+
data = value as NSNumber
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
func encode(_ value: UInt16) throws {
|
|
523
|
+
data = value as NSNumber
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
func encode(_ value: UInt32) throws {
|
|
527
|
+
data = value as NSNumber
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
func encode(_ value: UInt64) throws {
|
|
531
|
+
data = value as NSNumber
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
func encode<T>(_ value: T) throws where T: Encodable {
|
|
535
|
+
let encoder = _JSValueEncoder(optionalEncodingStrategy: optionalEncodingStrategy)
|
|
536
|
+
try value.encode(to: encoder)
|
|
537
|
+
data = encoder.data
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
extension SingleValueContainer: JSValueEncodingContainer {}
|
|
@@ -173,6 +173,17 @@ extension JSValueContainer {
|
|
|
173
173
|
public func getObject(_ key: String) -> JSObject? {
|
|
174
174
|
return jsObjectRepresentation[key] as? JSObject
|
|
175
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
|
+
}
|
|
176
187
|
}
|
|
177
188
|
|
|
178
189
|
@objc protocol BridgedJSValueContainer: NSObjectProtocol {
|
|
@@ -64,8 +64,52 @@ var nativeBridge = (function (exports) {
|
|
|
64
64
|
}
|
|
65
65
|
return newFormData;
|
|
66
66
|
};
|
|
67
|
-
const convertBody = async (body) => {
|
|
68
|
-
if (body instanceof
|
|
67
|
+
const convertBody = async (body, contentType) => {
|
|
68
|
+
if (body instanceof ReadableStream) {
|
|
69
|
+
const reader = body.getReader();
|
|
70
|
+
const chunks = [];
|
|
71
|
+
while (true) {
|
|
72
|
+
const { done, value } = await reader.read();
|
|
73
|
+
if (done)
|
|
74
|
+
break;
|
|
75
|
+
chunks.push(value);
|
|
76
|
+
}
|
|
77
|
+
const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
|
|
78
|
+
let position = 0;
|
|
79
|
+
for (const chunk of chunks) {
|
|
80
|
+
concatenated.set(chunk, position);
|
|
81
|
+
position += chunk.length;
|
|
82
|
+
}
|
|
83
|
+
let data = new TextDecoder().decode(concatenated);
|
|
84
|
+
let type;
|
|
85
|
+
if (contentType === 'application/json') {
|
|
86
|
+
try {
|
|
87
|
+
data = JSON.parse(data);
|
|
88
|
+
}
|
|
89
|
+
catch (ignored) {
|
|
90
|
+
// ignore
|
|
91
|
+
}
|
|
92
|
+
type = 'json';
|
|
93
|
+
}
|
|
94
|
+
else if (contentType === 'multipart/form-data') {
|
|
95
|
+
type = 'formData';
|
|
96
|
+
}
|
|
97
|
+
else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) {
|
|
98
|
+
type = 'image';
|
|
99
|
+
}
|
|
100
|
+
else if (contentType === 'application/octet-stream') {
|
|
101
|
+
type = 'binary';
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
type = 'text';
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
data,
|
|
108
|
+
type,
|
|
109
|
+
headers: { 'Content-Type': contentType || 'application/octet-stream' },
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
else if (body instanceof FormData) {
|
|
69
113
|
const formData = await convertFormData(body);
|
|
70
114
|
const boundary = `${Date.now()}`;
|
|
71
115
|
return {
|
|
@@ -432,8 +476,8 @@ var nativeBridge = (function (exports) {
|
|
|
432
476
|
console.time(tag);
|
|
433
477
|
try {
|
|
434
478
|
const { body, method } = request;
|
|
435
|
-
const { data: requestData, type, headers, } = await convertBody(body || undefined);
|
|
436
479
|
const optionHeaders = Object.fromEntries(request.headers.entries());
|
|
480
|
+
const { data: requestData, type, headers, } = await convertBody((options === null || options === void 0 ? void 0 : options.body) || body || undefined, optionHeaders['Content-Type'] || optionHeaders['content-type']);
|
|
437
481
|
const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', {
|
|
438
482
|
url: request.url,
|
|
439
483
|
method: method,
|
package/Capacitor.podspec
CHANGED
|
@@ -16,8 +16,8 @@ Pod::Spec.new do |s|
|
|
|
16
16
|
s.ios.deployment_target = '13.0'
|
|
17
17
|
s.authors = { 'Ionic Team' => 'hi@ionicframework.com' }
|
|
18
18
|
s.source = { git: 'https://github.com/ionic-team/capacitor.git', tag: package['version'] }
|
|
19
|
-
s.source_files = "#{prefix}Capacitor/Capacitor/*.{swift,h,m}", "#{prefix}Capacitor/Capacitor/
|
|
20
|
-
"#{prefix}Capacitor/Capacitor/Plugins/**/*.{swift,h,m}"
|
|
19
|
+
s.source_files = "#{prefix}Capacitor/Capacitor/*.{swift,h,m}", "#{prefix}Capacitor/Capacitor/Codable/*.{swift,h,m}",
|
|
20
|
+
"#{prefix}Capacitor/Capacitor/Plugins/*.{swift,h,m}", "#{prefix}Capacitor/Capacitor/Plugins/**/*.{swift,h,m}"
|
|
21
21
|
s.module_map = "#{prefix}Capacitor/Capacitor/Capacitor.modulemap"
|
|
22
22
|
s.resources = ["#{prefix}Capacitor/Capacitor/assets/native-bridge.js"]
|
|
23
23
|
s.dependency 'CapacitorCordova'
|
|
@@ -20,4 +20,5 @@ FOUNDATION_EXPORT const unsigned char CapacitorCordovaVersionString[];
|
|
|
20
20
|
#import <Cordova/CDVScreenOrientationDelegate.h>
|
|
21
21
|
#import <Cordova/CDVURLProtocol.h>
|
|
22
22
|
#import <Cordova/CDVViewController.h>
|
|
23
|
+
#import <Cordova/CDVWebViewProcessPoolFactory.h>
|
|
23
24
|
#import <Cordova/NSDictionary+CordovaPreferences.h>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
or more contributor license agreements. See the NOTICE file
|
|
4
|
+
distributed with this work for additional information
|
|
5
|
+
regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
to you under the Apache License, Version 2.0 (the
|
|
7
|
+
"License"); you may not use this file except in compliance
|
|
8
|
+
with the License. You may obtain a copy of the License at
|
|
9
|
+
|
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
|
|
12
|
+
Unless required by applicable law or agreed to in writing,
|
|
13
|
+
software distributed under the License is distributed on an
|
|
14
|
+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
KIND, either express or implied. See the License for the
|
|
16
|
+
specific language governing permissions and limitations
|
|
17
|
+
under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
#import <WebKit/WebKit.h>
|
|
21
|
+
|
|
22
|
+
@interface CDVWebViewProcessPoolFactory : NSObject
|
|
23
|
+
@property (nonatomic, retain) WKProcessPool* sharedPool;
|
|
24
|
+
|
|
25
|
+
+(instancetype) sharedFactory;
|
|
26
|
+
-(WKProcessPool*) sharedProcessPool;
|
|
27
|
+
@end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
or more contributor license agreements. See the NOTICE file
|
|
4
|
+
distributed with this work for additional information
|
|
5
|
+
regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
to you under the Apache License, Version 2.0 (the
|
|
7
|
+
"License"); you may not use this file except in compliance
|
|
8
|
+
with the License. You may obtain a copy of the License at
|
|
9
|
+
|
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
|
|
12
|
+
Unless required by applicable law or agreed to in writing,
|
|
13
|
+
software distributed under the License is distributed on an
|
|
14
|
+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
KIND, either express or implied. See the License for the
|
|
16
|
+
specific language governing permissions and limitations
|
|
17
|
+
under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
@import Foundation;
|
|
21
|
+
@import WebKit;
|
|
22
|
+
#import <Cordova/CDVWebViewProcessPoolFactory.h>
|
|
23
|
+
|
|
24
|
+
static CDVWebViewProcessPoolFactory *factory = nil;
|
|
25
|
+
|
|
26
|
+
@implementation CDVWebViewProcessPoolFactory
|
|
27
|
+
|
|
28
|
+
+ (instancetype)sharedFactory
|
|
29
|
+
{
|
|
30
|
+
static dispatch_once_t onceToken;
|
|
31
|
+
dispatch_once(&onceToken, ^{
|
|
32
|
+
factory = [[CDVWebViewProcessPoolFactory alloc] init];
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return factory;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
- (instancetype)init
|
|
39
|
+
{
|
|
40
|
+
if (self = [super init]) {
|
|
41
|
+
_sharedPool = [[WKProcessPool alloc] init];
|
|
42
|
+
}
|
|
43
|
+
return self;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
- (WKProcessPool*) sharedProcessPool {
|
|
47
|
+
return _sharedPool;
|
|
48
|
+
}
|
|
49
|
+
@end
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor/ios",
|
|
3
|
-
"version": "6.0.0-
|
|
3
|
+
"version": "6.0.0-beta.1",
|
|
4
4
|
"description": "Capacitor: Cross-platform apps with JavaScript and the web",
|
|
5
5
|
"homepage": "https://capacitorjs.com",
|
|
6
6
|
"author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"xc:build:xcframework": "scripts/build.sh xcframework"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"@capacitor/core": "^6.0.0-
|
|
29
|
+
"@capacitor/core": "^6.0.0-beta.1"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|