14
14
15
15
import Foundation
16
16
17
+ public protocol StructureCodingPassthroughTypeResolver {
18
+ static func isPassthroughType< T> ( _ t: T ) -> Bool
19
+ }
20
+
21
+ private struct NoPassthroughTypes : StructureCodingPassthroughTypeResolver {
22
+ static func isPassthroughType< T> ( _ t: T ) -> Bool {
23
+ return false
24
+ }
25
+ }
26
+
27
+ public protocol StructureCodingUncodedUnkeyed { }
28
+
17
29
extension DecodingError {
18
30
/// Returns a `.typeMismatch` error describing the expected type.
19
31
///
@@ -233,6 +245,9 @@ public class FirebaseDataEncoder {
233
245
/// The strategy to use for encoding keys. Defaults to `.useDefaultKeys`.
234
246
open var keyEncodingStrategy : KeyEncodingStrategy = . useDefaultKeys
235
247
248
+ /// A type that can resolve which types to 'pass through' - or leave alone while encoding. Defaults to not passing any types through.
249
+ open var passthroughTypeResolver : StructureCodingPassthroughTypeResolver . Type = NoPassthroughTypes . self
250
+
236
251
/// Contextual user-provided information for use during encoding.
237
252
open var userInfo : [ CodingUserInfoKey : Any ] = [ : ]
238
253
@@ -242,6 +257,7 @@ public class FirebaseDataEncoder {
242
257
let dataEncodingStrategy : DataEncodingStrategy
243
258
let nonConformingFloatEncodingStrategy : NonConformingFloatEncodingStrategy
244
259
let keyEncodingStrategy : KeyEncodingStrategy
260
+ let passthroughTypeResolver : StructureCodingPassthroughTypeResolver . Type
245
261
let userInfo : [ CodingUserInfoKey : Any ]
246
262
}
247
263
@@ -251,6 +267,7 @@ public class FirebaseDataEncoder {
251
267
dataEncodingStrategy: dataEncodingStrategy,
252
268
nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy,
253
269
keyEncodingStrategy: keyEncodingStrategy,
270
+ passthroughTypeResolver: passthroughTypeResolver,
254
271
userInfo: userInfo)
255
272
}
256
273
@@ -501,6 +518,7 @@ fileprivate struct _JSONKeyedEncodingContainer<K : CodingKey> : KeyedEncodingCon
501
518
}
502
519
503
520
public mutating func encode< T : Encodable > ( _ value: T , forKey key: Key ) throws {
521
+ if T . self is StructureCodingUncodedUnkeyed . Type { return }
504
522
self . encoder. codingPath. append ( key)
505
523
defer { self . encoder. codingPath. removeLast ( ) }
506
524
self . container [ _converted ( key) . stringValue] = try self . encoder. box ( value)
@@ -928,6 +946,8 @@ extension __JSONEncoder {
928
946
return ( value as! NSDecimalNumber )
929
947
} else if value is _JSONStringDictionaryEncodableMarker {
930
948
return try self . box ( value as! [ String : Encodable ] )
949
+ } else if let object = value as? NSObject , self . options. passthroughTypeResolver. isPassthroughType ( value) {
950
+ return object
931
951
}
932
952
933
953
// The value should request a container from the __JSONEncoder.
@@ -1166,6 +1186,9 @@ public class FirebaseDataDecoder {
1166
1186
/// The strategy to use for decoding keys. Defaults to `.useDefaultKeys`.
1167
1187
open var keyDecodingStrategy : KeyDecodingStrategy = . useDefaultKeys
1168
1188
1189
+ /// A type that can resolve which types to 'pass through' - or leave alone while decoding. Defaults to not passing any types through.
1190
+ open var passthroughTypeResolver : StructureCodingPassthroughTypeResolver . Type = NoPassthroughTypes . self
1191
+
1169
1192
/// Contextual user-provided information for use during decoding.
1170
1193
open var userInfo : [ CodingUserInfoKey : Any ] = [ : ]
1171
1194
@@ -1175,6 +1198,7 @@ public class FirebaseDataDecoder {
1175
1198
let dataDecodingStrategy : DataDecodingStrategy
1176
1199
let nonConformingFloatDecodingStrategy : NonConformingFloatDecodingStrategy
1177
1200
let keyDecodingStrategy : KeyDecodingStrategy
1201
+ let passthroughTypeResolver : StructureCodingPassthroughTypeResolver . Type
1178
1202
let userInfo : [ CodingUserInfoKey : Any ]
1179
1203
}
1180
1204
@@ -1184,6 +1208,7 @@ public class FirebaseDataDecoder {
1184
1208
dataDecodingStrategy: dataDecodingStrategy,
1185
1209
nonConformingFloatDecodingStrategy: nonConformingFloatDecodingStrategy,
1186
1210
keyDecodingStrategy: keyDecodingStrategy,
1211
+ passthroughTypeResolver: passthroughTypeResolver,
1187
1212
userInfo: userInfo)
1188
1213
}
1189
1214
@@ -1617,6 +1642,11 @@ fileprivate struct _JSONKeyedDecodingContainer<K : CodingKey> : KeyedDecodingCon
1617
1642
}
1618
1643
1619
1644
public func decode< T : Decodable > ( _ type: T . Type , forKey key: Key ) throws -> T {
1645
+ if type is StructureCodingUncodedUnkeyed . Type {
1646
+ // Note: not pushing and popping key to codingPath since the key is
1647
+ // not part of the decoded structure.
1648
+ return try T . init ( from: self . decoder)
1649
+ }
1620
1650
guard let entry = self . container [ key. stringValue] else {
1621
1651
throw DecodingError . keyNotFound ( key, DecodingError . Context ( codingPath: self . decoder. codingPath, debugDescription: " No value associated with key \( _errorDescription ( of: key) ) . " ) )
1622
1652
}
@@ -2505,12 +2535,12 @@ extension __JSONDecoder {
2505
2535
return try unbox_ ( value, as: type) as? T
2506
2536
}
2507
2537
2508
- fileprivate func unbox_( _ value: Any , as type : Decodable . Type ) throws -> Any ? {
2509
- if type == Date . self || type == NSDate . self {
2538
+ fileprivate func unbox_( _ value: Any , as _type : Decodable . Type ) throws -> Any ? {
2539
+ if _type == Date . self || _type == NSDate . self {
2510
2540
return try self . unbox ( value, as: Date . self)
2511
- } else if type == Data . self || type == NSData . self {
2541
+ } else if _type == Data . self || _type == NSData . self {
2512
2542
return try self . unbox ( value, as: Data . self)
2513
- } else if type == URL . self || type == NSURL . self {
2543
+ } else if _type == URL . self || _type == NSURL . self {
2514
2544
guard let urlString = try self . unbox ( value, as: String . self) else {
2515
2545
return nil
2516
2546
}
@@ -2520,14 +2550,17 @@ extension __JSONDecoder {
2520
2550
debugDescription: " Invalid URL string. " ) )
2521
2551
}
2522
2552
return url
2523
- } else if type == Decimal . self || type == NSDecimalNumber . self {
2553
+ } else if _type == Decimal . self || _type == NSDecimalNumber . self {
2524
2554
return try self . unbox ( value, as: Decimal . self)
2525
- } else if let stringKeyedDictType = type as? _JSONStringDictionaryDecodableMarker . Type {
2555
+ } else if let stringKeyedDictType = _type as? _JSONStringDictionaryDecodableMarker . Type {
2526
2556
return try self . unbox ( value, as: stringKeyedDictType)
2527
2557
} else {
2528
2558
self . storage. push ( container: value)
2529
2559
defer { self . storage. popContainer ( ) }
2530
- return try type. init ( from: self )
2560
+ if self . options. passthroughTypeResolver. isPassthroughType ( value) && type ( of: value) == _type {
2561
+ return value
2562
+ }
2563
+ return try _type. init ( from: self )
2531
2564
}
2532
2565
}
2533
2566
}
0 commit comments