A Swift macro that automatically generates default values for missing fields in Decodable structs.
DefaultedMacro is a Swift macro that helps you handle missing fields in JSON decoding by automatically providing default values. It's particularly useful when working with APIs that might omit certain fields, saving you from writing boilerplate code for handling optional values.
- Automatically generates
Decodableconformance - Provides sensible default values for common types:
String: empty string ("")Bool:falseInt:0Double:0.0- Arrays: empty array (
[]) - Dictionaries: empty dictionary (
[:])
- Debug assertions for missing fields
- Graceful handling of unsupported types
- Automatic handling of optional properties
- Support for nested types that also use
@DefaultedDecodable
Add the package to your Package.swift:
dependencies: [
.package(url: "https://github.com/yourusername/DefaultedMacro.git", from: "1.0.0")
],
targets: [
.target(
name: "YourTarget",
dependencies: ["DefaultedMacro"]
)
]Add the @DefaultedDecodable attribute to any struct that conforms to Decodable:
import DefaultedMacro
@DefaultedDecodable
struct Address: Decodable {
let street: String
let city: String
let zipCode: Int
}
@DefaultedDecodable
struct User: Decodable {
let name: String
let age: Int
let isActive: Bool
let address: Address // Nested type with @DefaultedDecodable
let scores: [Int]
let metadata: [String: String]
let customType: CustomType // Will use CustomType's own decoding logic
}The macro will automatically generate:
- A
CodingKeysenum for all properties - An
init(from:)method that handles missing fields with default values:String→""Int→0Bool→false[T]→[][K: V]→[:]- Optional types (
T?) remain optional and decode asnilif missing - Nested types that use
@DefaultedDecodablewill be decoded with their own defaults - Unsupported types will use their own
Decodableimplementation
// JSON with missing fields
let json = """
{
"name": "John",
"address": {
"street": "123 Main St"
}
}
"""
// Will decode successfully with default values
let user = try JSONDecoder().decode(User.self, from: json.data(using: .utf8)!)
print(user) // User(name: "John", age: 0, isActive: false, address: Address(street: "123 Main St", city: "", zipCode: 0), scores: [], metadata: [:])In debug mode, the macro will trigger an assertion failure when a field is missing, helping you catch missing fields during development:
#if DEBUG
assertionFailure("Missing key for field: age")
#endifThe macro automatically generates an init(from:) implementation that:
- Attempts to decode each field using
decodeIfPresent - If the field is missing, provides a default value based on the type:
String→""Bool→falseInt→0Double→0.0[T]→[][K: V]→[:]
- For nested types that use
@DefaultedDecodable, usesdecodeinstead ofdecodeIfPresentto let the nested type handle its own defaults - For unsupported types, uses their own
Decodableimplementation and rethrows any decoding errors - In debug builds, logs an assertion failure for missing fields
- Optional properties are handled by Swift's standard
Codableimplementation
In debug builds, the macro will log assertion failures when fields are missing:
// If "name" is missing in the JSON:
assertionFailure("Missing key for field: name")
// If an unsupported type fails to decode:
assertionFailure("Failed to decode field: customType of type: CustomType. Error: ...")This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.