Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/GottaGetSwifty/CodableWrappers
A Collection of PropertyWrappers to make custom Serialization of Swift Codable Types easy
https://github.com/GottaGetSwifty/CodableWrappers
codable ios property-wrappers swift swift-package-manager swift5-1
Last synced: 10 days ago
JSON representation
A Collection of PropertyWrappers to make custom Serialization of Swift Codable Types easy
- Host: GitHub
- URL: https://github.com/GottaGetSwifty/CodableWrappers
- Owner: GottaGetSwifty
- License: apache-2.0
- Created: 2019-09-18T03:26:06.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2024-05-05T19:42:58.000Z (6 months ago)
- Last Synced: 2024-10-01T01:11:54.359Z (about 1 month ago)
- Topics: codable, ios, property-wrappers, swift, swift-package-manager, swift5-1
- Language: Swift
- Homepage:
- Size: 205 KB
- Stars: 643
- Watchers: 12
- Forks: 39
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-swift - CodableWrappers - A Collection of PropertyWrappers to make custom Serialization of Codable Types easy. (Misc / Vim)
- awesome-swift - CodableWrappers - A Collection of PropertyWrappers to make custom Serialization of Codable Types easy. (Misc / Vim)
- awesome-swift - CodableWrappers - A Collection of PropertyWrappers to make custom Serialization of Swift Codable Types easy ` 📝 16 days ago ` (Misc [🔝](#readme))
- awesome-swift - CodableWrappers - A Collection of PropertyWrappers to make custom Serialization of Codable Types easy. (Misc / Vim)
README
# CodableWrappers
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FGottaGetSwifty%2FCodableWrappers%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/GottaGetSwifty/CodableWrappers)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FGottaGetSwifty%2FCodableWrappers%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/GottaGetSwifty/CodableWrappers)## Simplified Serialization with [Property Wrappers](https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md)
Make Complex Codable Serializate a breeze with declarative annotations!
```swift
@CustomCodable @SnakeCase
struct User: Codable {
let firstName: String
let lastName: String
@SecondsSince1970DateCoding
var joinDate: Date
@CustomCodingKey("data")
var imageData: Data
}
```3.0 released! [Release Notes](https://github.com/GottaGetSwifty/CodableWrappers/blob/master/ReleaseNotes.md)
---
## Documentation
[Full DocC documentation here](https://swiftpackageindex.com/GottaGetSwifty/CodableWrappers/documentation/codablewrappers) thanks to the Swift Package Index!
## Installation
### Swift Package Manager \***Preferred***
URL:
`https://github.com/GottaGetSwifty/CodableWrappers.git`
Manifest:
```swift
dependencies: [
.package(url: "https://github.com/GottaGetSwifty/CodableWrappers.git", .upToNextMajor(from: "3.0.0" )),
]
```### CocoaPods
```ruby
pod 'CodableWrappers', '~> 3.0.0'
```## Available CodingKey Macros
- [@CustomCodable](#customcodable) \*New in 3.0!*
- [@CustomCodingKey(String)](#customcodingkeystring) \*New in 3.0!*
- [@CodingKeyPrefix(String)](#codingkeyprefixstring) \*New in 3.0!*
- [@CodingKeySuffix(String)](#codingkeysuffixstring) \*New in 3.0!*
- [@CamelCase](#camelcase) \*New in 3.0!*
- [@FlatCase](#flatcase) \*New in 3.0!*
- [@PascalCase](#pascalcase) \*New in 3.0!*
- [@UpperCase](#uppercase) \*New in 3.0!*
- [@SnakeCase](#snakecase) \*New in 3.0!*
- [@CamelSnakeCase](#camelsnakecase) \*New in 3.0!*
- [@PascalSnakeCase](#pascalsnakecase) \*New in 3.0!*
- [@ScreamingSnakeCase](#screamingsnakecase) \*New in 3.0!*
- [@KebabCase](#kebabcase) \*New in 3.0!*
- [@CamelKebabCase](#camelkebabcase) \*New in 3.0!*
- [@PascalKebabCase](#pascalkebabcase) \*New in 3.0!*
- [@ScreamingKebabCase](#screamingkebabcase) \*New in 3.0!*## Available Property Wrappers
- [@EncodeNulls](#encodenulls)
- [Lossy Collections](#lossy-collections)
- [Empty Defaults](#empty-defaults)
- [Other Fallbacks](#other-fallbacks)
- [@OmitCoding](#omitcoding)
- [@Base64Coding](#base64coding)
- [@SecondsSince1970DateCoding](#secondssince1970datecoding)
- [@MillisecondsSince1970DateCoding](#millisecondssince1970datecoding)
- [@DateFormatterCoding\](#dateformattercodingdateformatterstaticcoder)
- [@ISO8601DateCoding](#iso8601datecoding)
- [@ISO8601DateFormatterCoding\](#iso8601dateformattercodingiso8601dateformatterstaticcoder)
- [@NonConformingFloatCoding\](#nonconformingfloatcodingvalueprovider)
- [@NonConformingDoubleCoding\](#nonconformingdoublecodingvalueprovider)
- [Bool Coding](#bool-coding)
- [Additional Customization](#additional-customization)
- [Property Mutability](#property-mutability)
- [Only Encoding or Decoding](#only-encoding-or-decoding)## Other Customization
- [Property Mutability](#property-mutability) \*New in 2.0*
- [Optionals](#optionals) \*New in 2.0*
- [Only Encoding or Decoding](#only-encoding-or-decoding)## Additional Links
- [Introduction Blog Post](https://www.getswifty.blog/blog/making-custom-serialization-a-breeze-in-swift-51-with-property-wrappers)
- [Custom Examples](https://github.com/GottaGetSwifty/CodableWrappers/blob/master/CustomExamples.md)
- [Release Notes](https://github.com/GottaGetSwifty/CodableWrappers/blob/master/ReleaseNotes.md)---
## @CustomCodable
Prerequisite for customizing CodingKeys
```swift
@Codable
struct MyType: Codable {
}
```## @CustomCodingKey\(String)
Use a custom String value for a Property's CodingKey
```swift
@CustomCodable
struct YourType: Codable {
@CodingKey("your-Custom_naming")
let firstProperty: String // Coding key will be "your-Custom_naming"
}
```## CodingKeyPrefix\(String)
Set a property's CodingKey with a custom value
```swift
@CustomCodable
struct YourType: Codable {
@CodingKeyPrefix("beta-")
let firstProperty: String // CodingKey will be "beta-firstProperty"
}
``````swift
@CustomCodable @CodingKeyPrefix("beta-")
struct YourType: Codable {
let firstProperty: String // CodingKey will be "beta-firstProperty"
}
```## CodingKeySuffix\(String)
/// Add a Suffix a property's CodingKey or Type's CodingKeys with a custom value
```swift
@CustomCodable
struct YourType: Codable {
@CodingKeySuffix("-beta")
let firstProperty: String // CodingKey will be "firstProperty-beta"
}
``````swift
@CustomCodable @CodingKeySuffix("-beta")
struct YourType: Codable {
let firstProperty: String // CodingKey will be "firstProperty-beta"
}
```## CamelCase
/// Make the CodingKey for a property or Type `camelCase`
```swift
@CustomCodable
struct YourType: Codable {
@CamelCase
let first-property: String // CodingKey will be "firstProperty"
}
``````swift
@CustomCodable @CamelCase
struct YourType: Codable {
let first-property: String // CodingKey will be "firstProperty"
}
```## @FlatCase
Make the CodingKey for a property or Type `flatcase`
```swift
@CustomCodable
struct YourType: Codable {
@FlatCase
let firstProperty: String // CodingKey will be "firstproperty"
}
``````swift
@CustomCodable @FlatCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "firstproperty"
}
```## @PascalCase
Make the CodingKey for a property or Type `PascalCase`
```swift
@CustomCodable
struct YourType: Codable {
@PascalCase
let firstProperty: String // CodingKey will be "FirstProperty"
}
``````swift
@CustomCodable @PascalCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "FirstProperty"
}
```## @UpperCase
Make the CodingKey for a property or Type `UPPERCASE`
```swift
@CustomCodable
struct YourType: Codable {
@UpperCase
let firstProperty: String // CodingKey will be "FIRSTPROPERTY"
}
``````swift
@CustomCodable @UpperCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "FIRSTPROPERTY"
}
```## @SnakeCase
Make the CodingKey for a property or Type `snake_case`
```swift
@CustomCodable
struct YourType: Codable {
@SnakeCase
let firstProperty: String // CodingKey will be "first_property"
}
``````swift
@CustomCodable @SnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "first_property"
}
```## @CamelSnakeCase
Make the CodingKey for a property or Type `camel_Snake_Case`
```swift
@CustomCodable
struct YourType: Codable {
@CamelSnakeCase
let firstProperty: String // CodingKey will be "first_Property"
}
``````swift
@CustomCodable @CamelSnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "first_Property"
}
```## @PascalSnakeCase
Make the CodingKey for a property or Type `Pascal_Snake_Case`
```swift
@CustomCodable
struct YourType: Codable {
@PascalSnakeCase
let firstProperty: String // CodingKey will be "First_Property"
}
``````swift
@CustomCodable @PascalSnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "First_Property"
}
```## @ScreamingSnakeCase
Make the CodingKey for a property or Type `SCREAMING_SNAKE_CASE`
```swift
@CustomCodable
struct YourType: Codable {
@ScreamingSnakeCase
let firstProperty: String // CodingKey will be "FIRST_PROPERTY"
}
``````swift
@CustomCodable @ScreamingSnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "FIRST_PROPERTY"
}
```## @KebabCase
Make the CodingKey for a property or Type `kebab-case`
```swift
@CustomCodable
struct YourType: Codable {
@KebabCase
let firstProperty: String // CodingKey will be "first-property"
}
``````swift
@CustomCodable @KebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "first-property"
}
```## @CamelKebabCase
Make the CodingKey for a property or Type `camel-Kebab-Case`
```swift
@CustomCodable
struct YourType: Codable {
@CamelKebabCase
let firstProperty: String // CodingKey will be "first-Property"
}
``````swift
@CustomCodable @CamelKebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "first-Property"
}
```## @PascalKebabCase
Make the CodingKey for a property or Type `Pascal-Kebab-Case`
```swift
@CustomCodable
struct YourType: Codable {
@PascalKebabCase
let firstProperty: String // CodingKey will be "First-Property"
}
``````swift
@CustomCodable @PascalKebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "First-Property"
}
```## @ScreamingKebabCase
Make the CodingKey for a property or Type `SCREAMING-KEBAB-CASE`
```swift
@CustomCodable
struct YourType: Codable {
@ScreamingKebabCase
let firstProperty: String // CodingKey will be "FIRST-PROPERTY"
}
``````swift
@CustomCodable @ScreamingKebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey will be "FIRST-PROPERTY"
}
```## @EncodeNulls
For a Property that should encode `null` for `nil` values
```swift
struct MyType: Codable {
@EncodeNulls
var myText: String? // Will not be omitted when nil, e.g. will encode to `null` in JSON and `$null` in PLISTs
}
```## Lossy Collections
```swift
@LossyArrayDecoding
@LossyDictionaryDecoding
@LossySetDecoding
```Filters null values during decoding without throwing an Error
```swift
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 // 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
```swift
struct MyType: Codable {
@FallbackEncoding
var int: Int? // will encode `0` when nil
@FallbackDecoding
var string: String // will decode to "" when value was missing/nil
@FallbackCoding
var array: [Int]? // will encode/decode to [] when missing/nil
}
```All Empty Values
```swift
BoolTrue
BoolFalse
EmptyBool
EmptyString
EmptyInt
EmptyInt16
EmptyInt32
EmptyInt64
EmptyInt8
EmptyUInt
EmptyUInt16
EmptyUInt32
EmptyUInt64
EmptyUInt8
EmptyCGFloat
EmptyDouble
EmptyFloat
EmptyFloat16
EmptyArray
EmptyDictionary
EmptySet
````Empty` defaults are available for most typical Foundation Types
## Other Fallbacks
Any other kind of default can be provided by a custom `FallbackValueProvider`
```swift
public struct DistantFutureDateProvider: FallbackValueProvider {
public static var defaultValue: Date { Date.distantFuture }
}struct MyType: Codable {
@FallbackEncoding
var updatedDate: Date?
}```
## @OmitCoding
For a Property you want to be ignore when (en/de)coding
```swift
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
```swift
struct MyType: Codable {
@Base64Coding
var myData: Data // Now encodes to a Base64 String
}
```## @SecondsSince1970DateCoding
For a Date property that should be serialized to SecondsSince1970
```swift
struct MyType: Codable {
@SecondsSince1970DateCoding
var myDate: Date // Now encodes to SecondsSince1970
}
```## @MillisecondsSince1970DateCoding
For a Date property that should be serialized to MillisecondsSince1970
```swift
struct MyType: Codable {
@MillisecondsSince1970DateCoding
var myDate: Date // Now encodes to MillisecondsSince1970
}
```## @DateFormatterCoding\
For other Date formats create a Type that adheres to the `DateFormatterStaticCoder` Protocol and use the convenience `@DateFormatterCoding` `typealias` or `@CodingUses`.
```swift
struct MyCustomDateCoder: DateFormatterStaticCoder {
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "MM:dd:yy H:mm:ss"
return formatter
}()
}struct MyType: Codable {
@DateFormatterCoding
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
```swift
struct MyType: Codable {
@ISO8601DateCoding
var myDate: Date // Now encodes to ISO8601
}
```## @ISO8601DateFormatterCoding\
For other Date formats create a Type that adheres to the `ISO8601DateFormatterStaticCoder` Protocol and use the convenience `@ISO8601DateFormatterCoding` `typealias` or `@CodingUses`.
```swift
@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
var myDate: Date // Now encodes with MyCustomISO8601DateFormatter's formatter
}
```## @NonConformingFloatCoding\
When using a non-conforming Float, create a Type that adheres to NonConformingDecimalValueProvider and use `@NonConformingFloatCoding`
```swift
struct MyNonConformingValueProvider: NonConformingDecimalValueProvider {
static var positiveInfinity: String = "100"
static var negativeInfinity: String = "-100"
static var nan: String = "-1"
}struct MyType: Codable {
@NonConformingFloatCoding
var myFloat: Float // Now encodes with the MyNonConformingValueProvider values for infinity/NaN
}
```## @NonConformingDoubleCoding\
When using a non-conforming Double, create a Type that adheres to NonConformingDecimalValueProvider and use `@NonConformingDoubleCoding`
```swift
struct MyNonConformingValueProvider: NonConformingDecimalValueProvider {
static var positiveInfinity: String = "100"
static var negativeInfinity: String = "-100"
static var nan: String = "-1"
}struct MyType: Codable {
@NonConformingDoubleCoding
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`
```swift
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`
```swift
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 `StaticCoder` `protocol`. You can then simply add `@CodingUses` to your property, or create a `typealias` to make it cleaner: `typealias YourCustomCoding = CodingUses`
In fact all the included Wrappers are built the same way!
### Full Example
```swift
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
var myData: Date // Now uses the NanosecondsSince9170Coder for serialization
}// Approach 2: CustomEncoding Property Wrapper typealias
typealias NanosecondsSince9170Coding = CodingUses
struct MyType: Codable {
@NanosecondsSince9170Coding
var myData: Date // Now uses the NanosecondsSince9170Coder for serialization
}
```Take a look at [these other examples](https://github.com/GottaGetSwifty/CodableWrappers/blob/master/CustomExamples.md) 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
```swift
struct MyType: Codable {
@Immutable @SecondsSince1970DateCoding
var createdAt: Date@SecondsSince1970DateCoding
var updatedAt: Datemutating func update() {
createdAt = Date() // ERROR - Cannot assign to property: 'createdAt' is a get-only property
updatedAt = Date() // Works!
}
}
```## Optionals
2.0 introduces `@OptionalCoding` to enable Optionals for a property.
```swift
struct MyType: Codable {
@SecondsSince1970DateCoding
var createdAt: Date@OptionalCoding
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`, etc.```swift
struct MyType: Encodable {
@SecondsSince1970DateEncoding
var myDate: Date
}struct MyType: Decodable {
@SecondsSince1970DateDecoding
var myDate: Date
}```
---
## Compatibility
- 3.x supports Swift 5.9
- 2.x supports Swift 5.2
- 1.x supports Swift 5.1---
## Contributions
If there is a standard Serialization or Coding Key strategy that could be added feel free to open an issue requesting it and/or submit a pull request with the new option.