@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.
@@ -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
- (FileManager.default.fileExists(atPath: appLocation.path, isDirectory: &isDirectory) == false || isDirectory.boolValue == false) {
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 FormData) {
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/Plugins/*.{swift,h,m}",
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>
@@ -25,4 +25,4 @@
25
25
  #import "CDVViewController.h"
26
26
  #import "CDVURLProtocol.h"
27
27
  #import "CDVScreenOrientationDelegate.h"
28
-
28
+ #import "CDVWebViewProcessPoolFactory.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-alpha.2",
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-alpha.2"
29
+ "@capacitor/core": "^6.0.0-beta.1"
30
30
  },
31
31
  "publishConfig": {
32
32
  "access": "public"