@@ -5510,23 +5510,102 @@ internal struct _DictionaryCodingKey: CodingKey {
55105510 internal let stringValue : String
55115511 internal let intValue : Int ?
55125512
5513- internal init ? ( stringValue: String ) {
5513+ internal init ( stringValue: String ) {
55145514 self . stringValue = stringValue
55155515 self . intValue = Int ( stringValue)
55165516 }
55175517
5518- internal init ? ( intValue: Int ) {
5518+ internal init ( intValue: Int ) {
55195519 self . stringValue = " \( intValue) "
55205520 self . intValue = intValue
55215521 }
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+ }
55225600}
55235601
55245602extension Dictionary : Encodable where Key: Encodable , Value: Encodable {
55255603 /// Encodes the contents of this dictionary into the given encoder.
55265604 ///
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.
55305609 ///
55315610 /// This function throws an error if any values are invalid for the given
55325611 /// encoder's format.
@@ -5537,16 +5616,26 @@ extension Dictionary: Encodable where Key: Encodable, Value: Encodable {
55375616 // Since the keys are already Strings, we can use them as keys directly.
55385617 var container = encoder. container ( keyedBy: _DictionaryCodingKey. self)
55395618 for (key, value) in self {
5540- let codingKey = _DictionaryCodingKey ( stringValue: key as! String ) !
5619+ let codingKey = _DictionaryCodingKey ( stringValue: key as! String )
55415620 try container. encode ( value, forKey: codingKey)
55425621 }
55435622 } else if Key . self == Int . self {
55445623 // Since the keys are already Ints, we can use them as keys directly.
55455624 var container = encoder. container ( keyedBy: _DictionaryCodingKey. self)
55465625 for (key, value) in self {
5547- let codingKey = _DictionaryCodingKey ( intValue: key as! Int ) !
5626+ let codingKey = _DictionaryCodingKey ( intValue: key as! Int )
55485627 try container. encode ( value, forKey: codingKey)
55495628 }
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+ }
55505639 } else {
55515640 // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily
55525641 // 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 {
56015690 let value = try container. decode ( Value . self, forKey: key)
56025691 self [ key. intValue! as! Key ] = value
56035692 }
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+ }
56045709 } else {
56055710 // We should have encoded as an array of alternating key-value pairs.
56065711 var container = try decoder. unkeyedContainer ( )
0 commit comments