@@ -5510,23 +5510,102 @@ internal struct _DictionaryCodingKey: CodingKey {
5510
5510
internal let stringValue : String
5511
5511
internal let intValue : Int ?
5512
5512
5513
- internal init ? ( stringValue: String ) {
5513
+ internal init ( stringValue: String ) {
5514
5514
self . stringValue = stringValue
5515
5515
self . intValue = Int ( stringValue)
5516
5516
}
5517
5517
5518
- internal init ? ( intValue: Int ) {
5518
+ internal init ( intValue: Int ) {
5519
5519
self . stringValue = " \( intValue) "
5520
5520
self . intValue = intValue
5521
5521
}
5522
+
5523
+ fileprivate init ( codingKey: CodingKey ) {
5524
+ self . stringValue = codingKey. stringValue
5525
+ self . intValue = codingKey. intValue
5526
+ }
5527
+ }
5528
+
5529
+ /// A type that can be converted to and from a coding key.
5530
+ ///
5531
+ /// With a `CodingKeyRepresentable` type, you can losslessly convert between a
5532
+ /// custom type and a `CodingKey` type.
5533
+ ///
5534
+ /// Conforming a type to `CodingKeyRepresentable` lets you opt in to encoding
5535
+ /// and decoding `Dictionary` values keyed by the conforming type to and from
5536
+ /// a keyed container, rather than encoding and decoding the dictionary as an
5537
+ /// unkeyed container of alternating key-value pairs.
5538
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5539
+ public protocol CodingKeyRepresentable {
5540
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5541
+ var codingKey : CodingKey { get }
5542
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5543
+ init ? < T: CodingKey > ( codingKey: T )
5544
+ }
5545
+
5546
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5547
+ extension RawRepresentable where Self: CodingKeyRepresentable , RawValue == String {
5548
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5549
+ public var codingKey : CodingKey {
5550
+ _DictionaryCodingKey ( stringValue: rawValue)
5551
+ }
5552
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5553
+ public init ? < T: CodingKey > ( codingKey: T ) {
5554
+ self . init ( rawValue: codingKey. stringValue)
5555
+ }
5556
+ }
5557
+
5558
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5559
+ extension RawRepresentable where Self: CodingKeyRepresentable , RawValue == Int {
5560
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5561
+ public var codingKey : CodingKey {
5562
+ _DictionaryCodingKey ( intValue: rawValue)
5563
+ }
5564
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5565
+ public init ? < T: CodingKey > ( codingKey: T ) {
5566
+ if let intValue = codingKey. intValue {
5567
+ self . init ( rawValue: intValue)
5568
+ } else {
5569
+ return nil
5570
+ }
5571
+ }
5572
+ }
5573
+
5574
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5575
+ extension Int : CodingKeyRepresentable {
5576
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5577
+ public var codingKey : CodingKey {
5578
+ _DictionaryCodingKey ( intValue: self )
5579
+ }
5580
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5581
+ public init ? < T: CodingKey > ( codingKey: T ) {
5582
+ if let intValue = codingKey. intValue {
5583
+ self = intValue
5584
+ } else {
5585
+ return nil
5586
+ }
5587
+ }
5588
+ }
5589
+
5590
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5591
+ extension String : CodingKeyRepresentable {
5592
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5593
+ public var codingKey : CodingKey {
5594
+ _DictionaryCodingKey ( stringValue: self )
5595
+ }
5596
+ @available ( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * )
5597
+ public init ? < T: CodingKey > ( codingKey: T ) {
5598
+ self = codingKey. stringValue
5599
+ }
5522
5600
}
5523
5601
5524
5602
extension Dictionary : Encodable where Key: Encodable , Value: Encodable {
5525
5603
/// Encodes the contents of this dictionary into the given encoder.
5526
5604
///
5527
- /// If the dictionary uses `String` or `Int` keys, the contents are encoded
5528
- /// in a keyed container. Otherwise, the contents are encoded as alternating
5529
- /// key-value pairs in an unkeyed container.
5605
+ /// If the dictionary uses keys that are `String`, `Int`, or a type conforming
5606
+ /// to `CodingKeyRepresentable`, the contents are encoded in a keyed container.
5607
+ /// Otherwise, the contents are encoded as alternating key-value pairs in an
5608
+ /// unkeyed container.
5530
5609
///
5531
5610
/// This function throws an error if any values are invalid for the given
5532
5611
/// encoder's format.
@@ -5537,16 +5616,26 @@ extension Dictionary: Encodable where Key: Encodable, Value: Encodable {
5537
5616
// Since the keys are already Strings, we can use them as keys directly.
5538
5617
var container = encoder. container ( keyedBy: _DictionaryCodingKey. self)
5539
5618
for (key, value) in self {
5540
- let codingKey = _DictionaryCodingKey ( stringValue: key as! String ) !
5619
+ let codingKey = _DictionaryCodingKey ( stringValue: key as! String )
5541
5620
try container. encode ( value, forKey: codingKey)
5542
5621
}
5543
5622
} else if Key . self == Int . self {
5544
5623
// Since the keys are already Ints, we can use them as keys directly.
5545
5624
var container = encoder. container ( keyedBy: _DictionaryCodingKey. self)
5546
5625
for (key, value) in self {
5547
- let codingKey = _DictionaryCodingKey ( intValue: key as! Int ) !
5626
+ let codingKey = _DictionaryCodingKey ( intValue: key as! Int )
5548
5627
try container. encode ( value, forKey: codingKey)
5549
5628
}
5629
+ } else if #available( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * ) ,
5630
+ Key . self is CodingKeyRepresentable . Type {
5631
+ // Since the keys are CodingKeyRepresentable, we can use the `codingKey`
5632
+ // to create `_DictionaryCodingKey` instances.
5633
+ var container = encoder. container ( keyedBy: _DictionaryCodingKey. self)
5634
+ for (key, value) in self {
5635
+ let codingKey = ( key as! CodingKeyRepresentable ) . codingKey
5636
+ let dictionaryCodingKey = _DictionaryCodingKey ( codingKey: codingKey)
5637
+ try container. encode ( value, forKey: dictionaryCodingKey)
5638
+ }
5550
5639
} else {
5551
5640
// Keys are Encodable but not Strings or Ints, so we cannot arbitrarily
5552
5641
// convert to keys. We can encode as an array of alternating key-value
@@ -5601,6 +5690,22 @@ extension Dictionary: Decodable where Key: Decodable, Value: Decodable {
5601
5690
let value = try container. decode ( Value . self, forKey: key)
5602
5691
self [ key. intValue! as! Key ] = value
5603
5692
}
5693
+ } else if #available( macOS 9999 , iOS 9999 , watchOS 9999 , tvOS 9999 , * ) ,
5694
+ let keyType = Key . self as? CodingKeyRepresentable . Type {
5695
+ // The keys are CodingKeyRepresentable, so we should be able to expect
5696
+ // a keyed container.
5697
+ let container = try decoder. container ( keyedBy: _DictionaryCodingKey. self)
5698
+ for codingKey in container. allKeys {
5699
+ guard let key: Key = keyType. init ( codingKey: codingKey) as? Key else {
5700
+ throw DecodingError . dataCorruptedError (
5701
+ forKey: codingKey,
5702
+ in: container,
5703
+ debugDescription: " Could not convert key to type \( Key . self) "
5704
+ )
5705
+ }
5706
+ let value : Value = try container. decode ( Value . self, forKey: codingKey)
5707
+ self [ key] = value
5708
+ }
5604
5709
} else {
5605
5710
// We should have encoded as an array of alternating key-value pairs.
5606
5711
var container = try decoder. unkeyedContainer ( )
0 commit comments