MacroCodableKit enhances your Codable experience in Swift, leveraging macros to generate precise and efficient code with zero additional memory allocations, thanks to the usage of pure (static) functions. It’s a comprehensive solution providing support for AllOf, OneOf, and customizable CodingKeys, extending the native Codable capabilities to keep up with OpenAPI specification seamlessly.


Codable with property wrappers is an alternative way to approach Codable, it’s quite a flexible approach and surely greatly reduces amount of boilerplate, at the same time it has its limitations

  • You can’t implement things like OneOf or AllOf from OpenAPI which is quite common in nowdays APIs
  • You can’t alter CodingKeys with @propertyWrapper
  • @propertyWrapper is an additional struct next to a property, so you might endup with twice as much allocations then needed
  • @propertyWrapper should be settable, so you can’t use let and you’ll probably endup with private(set)

There is a proposal though to allow let for @propertyWrappers which I hope will be eventually completed

Approaching Codable with Macros allows zero additional allocation, immutable properties, and the implementation of any desirable Codable strategy inside one tool – MacroCodableKit, which provides OneOf and AllOf coding implementations from OpenAPI spec, and allows CodingKeys altering via annotations.


  • OpenAPI Compatibility: Embrace OpenAPI specifications with @OneOfCodable, @AllOfCodable with ease.
  • Custom Coding Keys: Specify custom coding keys with CodingKey(_ key: String) annotation.
  • Skip Coding of a Property: Opt to ignore certain properties from being coded with OmitCoding.
  • Default Values and Strategies: Use @DefaultValue to handle failed decoding gracefully or @ValueStrategy to decode values your way, such as handling dates in various formats.
  • Custom Coding Strategies: Utilize @CustomCoding to define your encoding and decoding logic. Use build-in SafeDecoding to handle arrays and dictionaries in a safe manner.
  • Error Handling: Handle ignored decoding errors with CustomCodingDecoding.errorHandler & encoding errors with CustomCodingEncoding.errorHandler.
  • Zero additional allocations: Coding is done by predefined clean (static) functions, so there’s no need to allocate additional memory

… and more!


Basics – @Codable

Annotate a struct with @Codable, without additional annotations on properties it will generate default Codable conformance

Note Do not conform to Codable protocol yourself, it will prevent macro from generating code

struct User {
    let birthday: Double
    let name: String
    let isVerified: Bool

Let’s convert birthday to Date, change coding key of isVerified and make it default to false

Note Conform only to @Decodable if you don’t need encoding

struct User {
    let birthday: Date
    let name: String

    let isVerified: Bool

// json: { "birthday": 1696291200.0, "name": "Mikhail" }
// is_verified is not specified, so the default value is "false" as specified by `@DefaultValue`


@AllOfCodable describes OpenAPI AllOf relationship

Imagine you have SocialUser OpenAPI specification which inherits from User and have additional properties

    - $ref: '#components/schema/User'
    - type: object
          type: string
          type: boolean

In Swift code it could be implemented with just AllOfCodable annotation

struct SocialUser {
  struct Properties: Codable {
    let isPublic: Bool
    let username: String
  let user: User
  let additionalProperties: Properties


@OneOfCodable describes OpenAPI OneOf relationship

Note Only one associated value is expected in each enum case

enum PaymentMethod {
  case card(DebitCardPayload)
  case applePay(ApplePayPayload)
// json: either { "card": { ... DebitCardPayload ... } } or { "applePay": { ... ApplePayPayload ... } }



Annotate a property with @CodingKey(_ key: String), key will be used as CodingKey in decoding and encoding

struct User {
  let isVerified: Bool


Skip coding for a specific property with @OmitCoding() annotation

struct User {
  let isVerified: Bool

It might be useful when you describe an object, where each encoded property is a part of a http request body

struct Request {
  var endpoint: String { "/user/\(userID)/follow" }

  // We don't want to encode userID, since it's not part of the request body
  let userID: String
  let isFollowing: Bool


Use @DefaultValue<Provider: DefaultValueProvider>(_ type: Provider.Type) to provide default value if decoding fails

Warning @DefaultValue(_:) doesn’t affect encoding

struct User {
  @DefaultValue(BoolFalse) // property will be `false`, if value is absent or decoding fails
  let isVerified: Bool

Build-in presets:

  • BoolTrue – true by default, BoolFalse – false by default
  • IntZero – Int(0) by default
  • DoubleZero – Double(0) by default


Use @ValueStrategy<Strategy: ValueCodableStrategy>(_ strategy: Strategy.Type) to provide custom mapping

struct Upload {
    let document: Data

Can be combined with DefaultValue

struct Example {
    let string: String

Build-in presets

  • Dates:
    • ISO8601Default – handles dates in ISO8601 format, for example, 2023-10-03T10:15:30+00:00
    • ISO8601WithFullDate – handles dates with the full date in ISO8601 format, for example, 2023-10-03
    • ISO8601WithFractionalSeconds – handles dates with fractional seconds in ISO8601 format, for example, 2023-10-03T10:15:30.123+00:00
    • YearMonthDayDate – handles dates with full date, example: 2023-10-03
    • RFC2822Date – handles dates in “EEE, d MMM y HH:mm:ss zzz” (RFC2822), example: Tue, 3 Oct 2023 10:15:30 GMT
    • RFC3339Date – handles dates in “yyyy-MM-dd’T’HH:mm:ssZ” (RFC2822), example: 2023-10-03T10:15:30Z
    • TimestampedDate – converts timestamp either String or Double to Date, example: 1696291200.0
  • Misc:
    • Base64Data – converts base64 string to Data


@CustomCoding annotation allows specifying custom encoding and decoding strategies for properties. Attach it to a property and specify a type that contains the custom encoding/decoding logic. For instance, @CustomCoding(SafeDecoding) uses safeDecoding functions from CustomCodingDecoding and CustomCodingEncoding for handling arrays and dictionaries safely during encoding and decoding.

struct TaggedPhotos: Equatable {
    var photos: [Photo]

struct UserProfiles: Equatable {
    var profiles: [String: Profile]

// Corresponding JSON for TaggedPhotos with corrupted data
// {
//     "photos": [
//         { "url": "", "tag": "vacation" },
//         { "url": "", "tag": "family" },
//         "corruptedData"
//     ]
// }

// Corresponding JSON for UserProfiles with corrupted data
// {
//     "profiles": {
//         "john_doe": { "age": 25, "location": "NYC" },
//         "jane_doe": { "age": 28, "location": "LA" },
//         "corrupted_entry": "corruptedData"
//     }
// }

In the example above, @CustomCoding(SafeDecoding) will catch and forward any decoding errors caused by invalid decoding to CustomCodingDecoding.errorHandler, allowing the rest of the data to be decoded safely.


Swift Package Manager


  • swift-foundation by Apple Swift Foundation Team provided a profound understanding of Decoder, Encoder, and the handling of encodeIfPresent and decodeIfPresent functions which logic is required for the library such as MacroCodableKit.

  • BetterCodable by Mark Sands: This project has all the common and widespread use cases for Codable, which was adopted in MacroCodableKit. Especially key conversion case which turned out to be vital for SafeDecoding on dictionaries


View Github