Move your Codable and (En/De)coder customization to annotations!
struct YourType: Codable {
@MillisecondsSince1970DateCoding
var millisecondsDate: Date
@Base64Coding
var someData: Data
@OmitCoding
var temporaryProperty
}
Filters null values during decoding without throwing an Error
private struct LossyCollectionModel: Codable, Equatable {
@LossyArrayDecoding
var array: [String] // Ignores null values without throwing an Error
@LossyDictionaryDecoding
var dictionary: [String:String] // Ignores null values without throwing an Error
@LossySetDecoding
var set: Set<String> // Ignores null values without throwing an Error
}
Empty Defaults
When you want to encode/decode an empty value rather than decoding nil or omitting encoding
struct MyType: Codable {
@FallbackEncoding<EmptyInt>
var int: Int? // will encode `0` when nil
@FallbackDecoding<EmptyString>
var string: String // will decode to "" when value was missing/nil
@FallbackCoding<EmptyArray>
var array: [Int]? // will encode/decode to [] when missing/nil
}
Empty defaults are available for most typical Foundation Types
Other Fallbacks
Any other kind of default can be provided by a custom FallbackValueProvider
public struct DistantFutureDateProvider: FallbackValueProvider {
public static var defaultValue: Date { Date.distantFuture }
}
struct MyType: Codable {
@FallbackEncoding<DistantFutureDateProvider>
var updatedDate: Date?
}
@OmitCoding
For a Property you want to be ignore when (en/de)coding
struct MyType: Codable {
@OmitCoding
var myText: String? // Never encodes and ignores a value if one is in decoded data.
}
@Base64Coding
For a Data property that should be serialized to a Base64 encoded String
struct MyType: Codable {
@Base64Coding
var myData: Data // Now encodes to a Base64 String
}
@SecondsSince1970DateCoding
For a Date property that should be serialized to SecondsSince1970
struct MyType: Codable {
@SecondsSince1970DateCoding
var myDate: Date // Now encodes to SecondsSince1970
}
@MillisecondsSince1970DateCoding
For a Date property that should be serialized to MillisecondsSince1970
struct MyType: Codable {
@MillisecondsSince1970DateCoding
var myDate: Date // Now encodes to MillisecondsSince1970
}
@DateFormatterCoding<DateFormatterStaticCoder>
For other Date formats create a Type that adheres to the DateFormatterStaticCoder Protocol and use the convenience @DateFormatterCodingtypealias or @CodingUses<StaticCoder>.
struct MyCustomDateCoder: DateFormatterStaticCoder {
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "MM:dd:yy H:mm:ss"
return formatter
}()
}
struct MyType: Codable {
@DateFormatterCoding<MyCustomDateCoder>
var myDate: Date // Now encodes to the format: "MM:dd:yy H:mm:ss"
}
@ISO8601DateCoding
For a Date property that should be serialized using the ISO8601DateFormatter
struct MyType: Codable {
@ISO8601DateCoding
var myDate: Date // Now encodes to ISO8601
}
For other Date formats create a Type that adheres to the ISO8601DateFormatterStaticCoder Protocol and use the convenience @ISO8601DateFormatterCodingtypealias or @CodingUses<StaticCoder>.
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
struct MyCustomISO8601DateFormatter: ISO8601DateFormatterStaticCoder {
static let iso8601DateFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withDashSeparatorInDate]
return formatter
}()
}
struct MyType: Codable {
@ISO8601DateFormatterCoding<MyCustomISO8601DateFormatter>
var myDate: Date // Now encodes with MyCustomISO8601DateFormatter's formatter
}
@NonConformingFloatCoding<ValueProvider>
When using a non-conforming Float, create a Type that adheres to NonConformingDecimalValueProvider and use @NonConformingFloatCoding<NonConformingDecimalValueProvider>
struct MyNonConformingValueProvider: NonConformingDecimalValueProvider {
static var positiveInfinity: String = "100"
static var negativeInfinity: String = "-100"
static var nan: String = "-1"
}
struct MyType: Codable {
@NonConformingFloatCoding<MyNonConformingValueProvider>
var myFloat: Float // Now encodes with the MyNonConformingValueProvider values for infinity/NaN
}
@NonConformingDoubleCoding<ValueProvider>
When using a non-conforming Double, create a Type that adheres to NonConformingDecimalValueProvider and use @NonConformingDoubleCoding<NonConformingDecimalValueProvider>
struct MyNonConformingValueProvider: NonConformingDecimalValueProvider {
static var positiveInfinity: String = "100"
static var negativeInfinity: String = "-100"
static var nan: String = "-1"
}
struct MyType: Codable {
@NonConformingDoubleCoding<MyNonConformingValueProvider>
var myFloat: Float // Now encodes with the MyNonConformingValueProvider values for infinity/NaN
}
Bool Coding
Sometimes an API uses an Int or String for a booleans.
@BoolAsStringCoding
struct MyType: Codable {
@BoolAsStringCoding
var myBool: Bool // Now encodes/decodes as a String. `"true"` for `true` and `"false"` for `false`. (Values are lower-cased before decoding)
}
@BoolAsIntCoding
struct MyType: Codable {
@BoolAsIntCoding
var myBool: Bool // Now encodes/decodes as an Int. `1` for `true` and `0` for `false`.
}
Additional Customization
The architecture was built with extensibility in mind so Implementing your own custom coding is as simple as adhering to the StaticCoderprotocol. You can then simply add @CodingUses<YourCustomCoder> to your property, or create a typealias to make it cleaner: typealias YourCustomCoding = CodingUses<YourCustomCoder>
In fact all the included Wrappers are built the same way!
Full Example
struct NanosecondsSince9170Coder: StaticCoder {
static func decode(from decoder: Decoder) throws -> Date {
let nanoSeconds = try Double(from: decoder)
let seconds = nanoSeconds * 0.000000001
return Date(secondsSince1970: seconds)
}
static func encode(value: Date, to encoder: Encoder) throws {
let nanoSeconds = value.secondsSince1970 / 0.000000001
return try nanoSeconds.encode(to: encoder)
}
}
// Approach 1: CustomCoding
struct MyType: Codable {
@CodingUses<NanosecondsSince9170Coder>
var myData: Date // Now uses the NanosecondsSince9170Coder for serialization
}
// Approach 2: CustomEncoding Property Wrapper typealias
typealias NanosecondsSince9170Coding = CodingUses<NanosecondsSince9170Coder>
struct MyType: Codable {
@NanosecondsSince9170Coding
var myData: Date // Now uses the NanosecondsSince9170Coder for serialization
}
In 2.0 all wrappers are Mutable by default and can be made Immutable via Property Wrapper Composition
struct MyType: Codable {
@Immutable @SecondsSince1970DateCoding
var createdAt: Date
@SecondsSince1970DateCoding
var updatedAt: Date
mutating func update() {
createdAt = Date() // ERROR - Cannot assign to property: 'createdAt' is a get-only property
updatedAt = Date() // Works!
}
}
Optionals
2.0 introduces @OptionalCoding<StaticCodingWrapper> to enable Optionals for a property.
struct MyType: Codable {
@SecondsSince1970DateCoding
var createdAt: Date
@OptionalCoding<SecondsSince1970DateCoding>
var updatedAt: Date?
}
Only Encoding or Decoding
Sometimes you are only able/wanting to implement Encoding or Decoding.
To enable this, (where practical/possible), all of the included Wrappers have Encoding and Decoding variants
Change Coder to Encoder/Decoder or Coding to Encoding/Decoding to implement only one
E.g. @Base64Encoding, @SecondsSince1970DateDecoding, @EncodingUses<ACustomEncoder>, etc.
struct MyType: Encodable {
@SecondsSince1970DateEncoding
var myDate: Date
}
struct MyType: Decodable {
@SecondsSince1970DateDecoding
var myDate: Date
}
Contributions
If there is a standard Serialization strategy that could be added feel free to open an issue requesting it and/or submit a pull request with the new option.
CodableWrappers
Simplified Serialization with Property Wrappers
Move your Codable and (En/De)coder customization to annotations!
2.0’s released! Release Notes
Installation
Swift Package Manager *Preferred*
URL:
https://github.com/GottaGetSwifty/CodableWrappers.git
Manifest:
CocoaPods
Info
Available Property Wrappers
Other Customization
Additional Links
Advantages
encode(to: Encoder)
orinit(with decoder)
for your whole TypeCompatibility
2.x has a Swift 5.2 as it’s minimum. 5.1 is available on 1.x
@EncodeNulls
For a Property that should encode
null
fornil
valuesLossy Collections
Filters null values during decoding without throwing an Error
Empty Defaults
When you want to encode/decode an empty value rather than decoding nil or omitting encoding
All Empty Values
Empty
defaults are available for most typical Foundation TypesOther Fallbacks
Any other kind of default can be provided by a custom
FallbackValueProvider
@OmitCoding
For a Property you want to be ignore when (en/de)coding
@Base64Coding
For a Data property that should be serialized to a Base64 encoded String
@SecondsSince1970DateCoding
For a Date property that should be serialized to SecondsSince1970
@MillisecondsSince1970DateCoding
For a Date property that should be serialized to MillisecondsSince1970
@DateFormatterCoding<DateFormatterStaticCoder>
For other Date formats create a Type that adheres to the
DateFormatterStaticCoder
Protocol and use the convenience@DateFormatterCoding
typealias
or@CodingUses<StaticCoder>
.@ISO8601DateCoding
For a Date property that should be serialized using the ISO8601DateFormatter
@ISO8601DateFormatterCoding<ISO8601DateFormatterStaticCoder>
For other Date formats create a Type that adheres to the
ISO8601DateFormatterStaticCoder
Protocol and use the convenience@ISO8601DateFormatterCoding
typealias
or@CodingUses<StaticCoder>
.@NonConformingFloatCoding<ValueProvider>
When using a non-conforming Float, create a Type that adheres to NonConformingDecimalValueProvider and use
@NonConformingFloatCoding<NonConformingDecimalValueProvider>
@NonConformingDoubleCoding<ValueProvider>
When using a non-conforming Double, create a Type that adheres to NonConformingDecimalValueProvider and use
@NonConformingDoubleCoding<NonConformingDecimalValueProvider>
Bool Coding
Sometimes an API uses an
Int
orString
for a booleans.@BoolAsStringCoding
@BoolAsIntCoding
Additional Customization
The architecture was built with extensibility in mind so Implementing your own custom coding is as simple as adhering to the
StaticCoder
protocol
. You can then simply add@CodingUses<YourCustomCoder>
to your property, or create atypealias
to make it cleaner:typealias YourCustomCoding = CodingUses<YourCustomCoder>
In fact all the included Wrappers are built the same way!
Full Example
Take a look at these other examples to see what else is possible.
Property Mutability
In 2.0 all wrappers are Mutable by default and can be made Immutable via Property Wrapper Composition
Optionals
2.0 introduces
@OptionalCoding<StaticCodingWrapper>
to enable Optionals for a property.Only Encoding or Decoding
Sometimes you are only able/wanting to implement Encoding or Decoding.
To enable this, (where practical/possible), all of the included Wrappers have Encoding and Decoding variants
Change Coder to Encoder/Decoder or Coding to Encoding/Decoding to implement only one E.g.
@Base64Encoding
,@SecondsSince1970DateDecoding
,@EncodingUses<ACustomEncoder>
, etc.Contributions
If there is a standard Serialization strategy that could be added feel free to open an issue requesting it and/or submit a pull request with the new option.