@@ -37,12 +37,16 @@ public struct KeyValueEncoder: Sendable {
3737 /// Contextual user-provided information for use during encoding.
3838 public var userInfo : [ CodingUserInfoKey : any Sendable ]
3939
40- /// The strategy to use for encoding `nil`. Defaults to `Optional<Any>.none` which can be cast to any optional type .
41- public var nilEncodingStrategy : NilEncodingStrategy = . default
40+ /// The strategy to use for encoding Date types .
41+ public var dateEncodingStrategy : DateEncodingStrategy = . date
4242
4343 /// The strategy to use for encoding each types keys.
4444 public var keyEncodingStrategy : KeyEncodingStrategy = . useDefaultKeys
4545
46+ /// The strategy to use for encoding `nil`. Defaults to `Optional<Any>.none` which can be cast to any optional type.
47+ public var nilEncodingStrategy : NilEncodingStrategy = . default
48+
49+
4650 /// Initializes `self` with default strategies.
4751 public init ( ) {
4852 self . userInfo = [ : ]
@@ -67,6 +71,21 @@ public struct KeyValueEncoder: Sendable {
6771 /// A key encoding strategy that doesn’t change key names during encoding.
6872 case useDefaultKeys
6973 }
74+
75+ public enum DateEncodingStrategy : Sendable {
76+
77+ /// Encodes dates by directly casting to Any.
78+ case date
79+
80+ /// Encodes dates from ISO8601 strings.
81+ case iso8601( options: ISO8601DateFormatter . Options = [ . withInternetDateTime] )
82+
83+ /// Encodes dates to Int in terms of milliseconds since midnight UTC on January 1, 1970.
84+ case millisecondsSince1970
85+
86+ /// Encodes dates to Int in terms of seconds since midnight UTC on January 1, 1970.
87+ case secondsSince1970
88+ }
7089}
7190
7291/// Strategy used to encode and decode nil values.
@@ -96,6 +115,12 @@ extension KeyValueEncoder: TopLevelEncoder {
96115
97116extension KeyValueEncoder {
98117
118+ struct EncodingStrategy {
119+ var optionals : NilEncodingStrategy
120+ var keys : KeyEncodingStrategy
121+ var dates : DateEncodingStrategy
122+ }
123+
99124 static func makePlistCompatible( ) -> KeyValueEncoder {
100125 var encoder = KeyValueEncoder ( )
101126 encoder. nilEncodingStrategy = . stringNull
@@ -161,15 +186,11 @@ private extension KeyValueEncoder.NilEncodingStrategy {
161186
162187private extension KeyValueEncoder {
163188
164- struct EncodingStrategy {
165- var optionals : NilEncodingStrategy
166- var keys : KeyEncodingStrategy
167- }
168-
169189 var strategy : EncodingStrategy {
170190 EncodingStrategy (
171191 optionals: nilEncodingStrategy,
172- keys: keyEncodingStrategy
192+ keys: keyEncodingStrategy,
193+ dates: dateEncodingStrategy
173194 )
174195 }
175196
@@ -217,7 +238,7 @@ private extension KeyValueEncoder {
217238 }
218239
219240 func encodeToValue< T> ( _ value: T ) throws -> EncodedValue where T: Encodable {
220- guard let encoded = EncodedValue ( value) else {
241+ guard let encoded = EncodedValue . makeValue ( for : value, using : strategy ) else {
221242 try value. encode ( to: self )
222243 return try getEncodedValue ( )
223244 }
@@ -317,7 +338,7 @@ private extension KeyValueEncoder {
317338 }
318339
319340 func encode< T: Encodable > ( _ value: T , forKey key: Key ) throws {
320- if let val = EncodedValue ( value) {
341+ if let val = EncodedValue . makeValue ( for : value, using : strategy ) {
321342 setValue ( val, forKey: key)
322343 return
323344 }
@@ -447,7 +468,7 @@ private extension KeyValueEncoder {
447468 }
448469
449470 func encode< T: Encodable > ( _ value: T ) throws {
450- if let val = EncodedValue ( value) {
471+ if let val = EncodedValue . makeValue ( for : value, using : strategy ) {
451472 appendValue ( val)
452473 return
453474 }
@@ -566,7 +587,7 @@ private extension KeyValueEncoder {
566587 }
567588
568589 func encode< T> ( _ value: T ) throws where T: Encodable {
569- if let encoded = EncodedValue ( value) {
590+ if let encoded = EncodedValue . makeValue ( for : value, using : strategy ) {
570591 self . value = encoded
571592 return
572593 }
@@ -687,20 +708,32 @@ struct AnyCodingKey: CodingKey {
687708
688709extension KeyValueEncoder . EncodedValue {
689710
690- static func isSupportedValue( _ value: Any ) -> Bool {
691- switch value {
692- case is Data : return true
693- case is Date : return true
694- case is URL : return true
695- case is Decimal : return true
696- default : return false
711+ static func makeValue( for value: Any , using strategy: KeyValueEncoder . EncodingStrategy ) -> Self ? {
712+ if let dataValue = value as? Data {
713+ return . value( dataValue)
714+ } else if let dateValue = value as? Date {
715+ return makeValue ( for: dateValue, using: strategy. dates)
716+ } else if let urlValue = value as? URL {
717+ return . value( urlValue)
718+ } else if let decimalValue = value as? Decimal {
719+ return . value( decimalValue)
720+ } else {
721+ return nil
697722 }
698723 }
699724
700- init ? ( _ value: Any ) {
701- guard Self . isSupportedValue ( value) else {
702- return nil
725+ static func makeValue( for date: Date , using strategy: KeyValueEncoder . DateEncodingStrategy ) -> Self ? {
726+ switch strategy {
727+ case . date:
728+ return . value( date)
729+ case . iso8601( options: let options) :
730+ let f = ISO8601DateFormatter ( )
731+ f. formatOptions = options
732+ return . value( f. string ( from: date) )
733+ case . millisecondsSince1970:
734+ return . value( Int ( date. timeIntervalSince1970 * 1000 ) )
735+ case . secondsSince1970:
736+ return . value( Int ( date. timeIntervalSince1970) )
703737 }
704- self = . value( value)
705738 }
706739}
0 commit comments