Hello!
I'd like to announce version 1.0.0 of a library I'm calling CodableWrappers.
It uses Property Wrappers to greatly simplify non-default serialization of Codable Types. Check out the Readme or blog post for more information, but I'll give a quick overview here as well.
The basic idea is to move custom encoding/decoding to Property Wrappers. So e.g. if you have a basic Book:
struct Book: Codable {
/// Encodes with secondsSince1970
let published: Date
let uuid: String
let title: String
let author: String
}
Rather than customizing every encoder/decoder you use
let jsonEncoder = JSONEncoder()
jsonEncoder.dateEncodingStrategy = .secondsSince1970
let jsonDecoder = JSONDecoder()
jsonDecoder.dateDecodingStrategy = .secondsSince1970
Or customizing the Codable implementation
struct Book: Codable {
let count: Int
let title: String
let author: String
/// Seconds Since 1970
let released: Date
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.count = try values.decode(Int.self, forKey: .count)
self.title = try values.decode(Int.self, forKey: .title)
self.author = try values.decode(Int.self, forKey: .author)
let secondsSince = values.decode(Double.self, forKey: .released)
self.released = Date(timeIntervalSince1970: secondsSince)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(count, forKey: .count)
try container.encode(title, forKey: .title)
try container.encode(author, forKey: .author)
try container.encode(released.timerIntervalSince1970, forKey: .released)
}
}
You can simply add a Property Wrapper
struct Book: Codable {
@SecondsSince1970DateCoding
var published: Date
let uuid: String
let title: String
let author: String
}
The need for this is especially clear if you have a data structure that uses more than one kind of encoding
struct Catalog: Codable {
@MillisecondsSince1970DateCoding
var updatedAt: Date
let books: [Book]
}
struct Book: Codable {
@SecondsSince1970DateCoding
var published: Date
let uuid: String
let title: String
let author: String
}
This approach has several advantages compared to the status quo:
- Declarative
- Extendable
- Declare once for all Encoders and Decoders. (e.g. JSONEncoder and PropertyListEncoder)
- Can customize (de/en)coding without overriding encode(to: Encoder) or init(with decoder)
- Multiple (de/en)coding strategies within a data structure
- Fully Cross Platform
The library currently includes analogs for the customizations available in JSON(En/De)Coder as well as ways to easily write your own.
Feedback and ideas for additional features are welcome!