MetaCodable: generating Swift Codable conformances with macros
Supercharge Swift's Codable implementations with macros meta-programming.
At a glance
- What is it?
- MetaCodable is a Swift macro package that generates Codable conformances from attributes attached to your properties, so a model can declare a coding key, a default value, a fallback key or a custom coder per property instead of hand writing CodingKeys and helper types. It ships as a Swift package with CocoaPods support and a build tool plugin for protocol conformances.
- Who is it for?
- MetaCodable suits a Swift team whose models talk to a JSON API with awkward key names, optional fields or a nested envelope, because the attribute per property replaces the hand written key enums and helper coders that such a codebase accumulates.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The idea: attributes instead of key enums
MetaCodable exposes custom macros that generate dynamic Codable implementations, and the core of the framework is the Codable macro, which generates the implementation with the help of data supplied by the other macros.
The problem it addresses is visible in any Swift codebase that talks to a JSON API with keys that do not match the property names. The conventional solution is a nested CodingKeys enum listing every property, and when the model needs a default value, a fallback key, a nested path or a custom conversion for one field, the conventional solution grows hand written initialisers and helper types alongside it.
MetaCodable moves that information onto the property itself, as an attribute. The README's own example makes the contrast concrete, comparing what the official documentation asks you to write for a model with two renamed fields against what the macro version looks like, where the mapping is two attributes and nothing else.
So the library's pitch is not that Swift's Codable is bad. It is that the attributes you need for the awkward cases are not expressible in the standard conformance, and that writing them by hand does not scale across a large model layer.
The package is MIT licensed, published on Swift Package Index, with the repository under the SwiftyLab organisation.
Keys, structure and fallback names
The feature list groups into three families, and the first is about where data lives in the encoded form.
The CodedAt attribute with a single argument sets a custom CodingKey value for one variable, so you do not have to write all the CodingKeys values for the type. The CodedIn attribute and CodedAt together let you build a flattened model for nested key values, which is what you need when the JSON nests and your Swift model does not. And CodedAt with no arguments lets you compose several Codable types into one, which is the way to express a payload that spreads fields across two types.
The CodedAs attribute covers naming and case values. It can specify different case values, and the README notes it can also carry a case value or protocol type identifier that is not a String. CodedAt specifies the enum case or protocol type identifier path, and ContentAt specifies the path of the case content.
Then there are the fallback keys, with the CodedAs attribute taking two arguments, which allow data to be read from additional fallback keys. That is the feature that saves the day when an API renames a field but old records still use the old name.
The enum handling has its own feature. The UnTagged attribute allows decoding and encoding of enums that lack distinct identifiers for each case, and the ContentAt attribute works with the case content path. That combination is what lets you decode a discriminated payload whose cases carry their own content without a separate identifier field.
The README also points to a limitations page for the macro, which is the document to read before you rely on the corner cases.
Defaults, custom coders and the things you can ignore
The second family of features is about what happens when decoding fails or when a value is simply absent.
The Default attribute with one argument provides a default value in case of decoding failures, and the variant with an ifMissing argument applies the default only when the value is missing, so a malformed value still produces an error rather than being silently replaced. A third variant takes different defaults for a missing value and for other errors, so the two failure modes can be treated differently.
The fourth family is about custom strategies and selective exclusion.
The HelperCoder mechanism allows custom decoding and encoding strategies, used through the CodedBy attribute and its variants, and the README gives examples such as a lossy sequence coder. The Codable macro takes a commonStrategies parameter, so a strategy such as a value coder can be applied to all the properties of a type at once, which removes the need to annotate every property with the same thing.
On the exclusion side, there are attributes to ignore specific properties or enum cases from decoding, from encoding, or from both, and a conditional variant that skips encoding based on a custom condition. A further attribute ignores all the initialised properties of a type or case unless a coding attribute is explicitly attached to them, which is a way of saying that a property is set in code rather than decoded from data, and that a type should opt in rather than out.
And there is a naming feature that is about convention rather than data: camel case names for variables following the Swift API design guidelines, while still allowing a type or case to work with different key styles through the CodingKeys attribute.
Dynamic and protocol conformances
The most interesting feature in the list is the one about protocols, because it goes beyond generating a conformance for a concrete type.
The framework can generate protocol decoding and encoding helpers with a MetaProtocolCodable build tool plugin, from types that conform to DynamicCodable. A dynamic codable is a type whose coding behaviour is decided at runtime rather than by a fixed conformance, and generating the protocol helper from it is what lets a generic decoding layer work with types you did not write the helper for.
The build tool plugin is a separate piece of machinery for that reason. A macro can only expand in the file where it is used, so anything that needs to know about the whole module, such as generating helpers for every conforming type, has to run as a build plugin instead. The repository has a plugins directory and a separate core for the plugin, and the package publishes both as separate products.
The practical benefit is for codebases with a decoding layer that dispatches on a protocol, a common shape in apps that decode heterogeneous server responses. Hand writing a helper for each conforming type is exactly the repetition this project exists to remove.
Installing it
There are two supported routes and both are short.
With the Swift Package Manager, which is integrated into the swift compiler, you add the package to the dependencies value of your Package.swift.
.package(url: "https://github.com/SwiftyLab/MetaCodable.git", from: "1.0.0"),Then you add the module product as a dependency to the targets you want to use it in.
.product(name: "MetaCodable", package: "MetaCodable"),With CocoaPods, you specify the pod in your Podfile.
pod 'MetaCodable'The two route requirement is worth noting. The macros are the part that matters and they need a toolchain with macro support, which means a modern Swift, and the requirements table in the README says so explicitly: Swift 5.9 for the Apple platforms and for Linux, and 5.9.1 for Windows.
The platform floor is listed alongside: iOS 13 or later, macOS 10.15 or later, tvOS 13 or later and watchOS 6 or later on Apple platforms, with both the Swift Package Manager and CocoaPods marked as fully tested, Linux with the Swift Package Manager only, and Windows with the Swift Package Manager only. The CocoaPods gap on Linux and Windows is a CocoaPods limitation rather than a project one, but it is the kind of thing worth knowing before you pick an installation route.
The example, before and after
The README's first example is the one that makes the case.
In the official documentation, defining custom CodingKeys for two fields of a model means writing an enumeration with a case for every property, including the ones whose names do not change.
struct Landmark: Codable {
var name: String
var foundingYear: Int
var location: Coordinate
var vantagePoints: [Coordinate]
enum CodingKeys: String, CodingKey {
case name = "title"
case foundingYear = "founding_date"
case location
case vantagePoints
}
}With MetaCodable the same model is the attributes and the properties.
@Codable
struct Landmark {
@CodedAt("title")
var name: String
@CodedAt("founding_date")
var foundingYear: Int
var location: Coordinate
var vantagePoints: [Coordinate]
}Two things are visible in the difference. The properties that do not need a custom key are simply left alone, and the ones that do carry the mapping at the point of use, next to the type they belong to.
The second thing is the attribute on the type itself. The Codable macro replaces the conformance, so the model no longer declares Codable at all, which is what lets the macro generate the whole implementation, including the parts a hand written conformance would have to cover for every case of the enumeration and every optional.
Packaging, tooling and maintenance
The repository is a multi product Swift package, and the split is visible in the files at the root. There are five podspec files, one for the main package, one for the helper coders, one for the macros, and two for the build tool plugin and its core. There are two package manifests, one for the current Swift and one for the earlier Swift 5 layout, and separate directories for the plugins, the sources, the tests, the utilities and the examples.
The examples directory has its own package manifest and a Podfile, so the examples are built as a separate SwiftPM package rather than as part of the library.
The JavaScript side is the continuous integration glue. The package manifest lists the five pods, depends on a shared CI script package from the same organisation, and defines the commands: build, test, format with the swift format tool, a pod lint script, and the documentation commands for previewing, building and serving the generated documentation.
The licence is MIT, with a licence file at the root and a code of conduct. The releases are v1.6.1 from 2026-06-24, v1.6.0 from 2026-01-20 and v1.5.0 from 2025-07-08, and the last push was on 2026-09-26. The badges state the supported toolchain as Swift 5.9 and later, all platforms supported, and there are badges for the Swift Package Manager and for CocoaPods.
One practical detail to plan for. Because this is a macro package, adding it is a build time change, and the macro expansion is what generates your conformance. That means the compiler, not your code, is where a mistake in the attributes shows up, and it means a toolchain upgrade can change the generated code. The limitations page and the changelog are the two documents to read when something surprising appears.
Editorial conclusion
MetaCodable suits a Swift team whose models talk to a JSON API with awkward key names, optional fields or a nested envelope, because the attribute per property replaces the hand written key enums and helper coders that such a codebase accumulates. It is the wrong choice if your models are already plain and conformances are written by hand, since the macro adds a build dependency for no gain, and it is the wrong choice if you cannot move to Swift macros, which means a toolchain older than 5.9. Verify first that the key mapping your API needs is expressible with the available attributes, because the macro generates from those attributes and cannot invent a mapping, and read the limitations page before you rely on it for the more unusual cases.
Frequently asked questions
What does MetaCodable do?
It provides Swift macros that generate Codable implementations, with the core Codable macro producing the conformance from data supplied by the other macros, so custom keys, defaults, fallback keys and custom coders are declared as attributes on the properties.
How do I install MetaCodable?
With the Swift Package Manager, by adding the package to your Package.swift dependencies and the module product to your targets, or with CocoaPods by adding the pod to your Podfile. CocoaPods is listed as fully tested on the Apple platforms, with Linux and Windows using the Swift Package Manager.
What is the difference between the Default attribute variants?
The plain form supplies a default whenever decoding fails, the ifMissing form applies it only when the value is missing so that malformed data still errors, and the form taking both applies different defaults for a missing value and for other errors.
What does the MetaProtocolCodable build tool plugin do?
It generates protocol decoding and encoding helpers from types conforming to DynamicCodable, which a macro alone cannot do because a macro only expands in the file where it is used.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/swiftylab-metacodable)Was this useful?