Skip to content

feat: added common strategies applicable to all properties #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 21, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Examples/App/Sources/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import SwiftUI
import MetaCodable
import HelperCoders
import MetaCodable
import SwiftUI

public struct ContentView: View {
public init() {}
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// swift-tools-version: 6.0
// swift-format-ignore-file

import CompilerPluginSupport
import Foundation
import PackageDescription
import CompilerPluginSupport

let package = Package(
name: "MetaCodable",
3 changes: 2 additions & 1 deletion Package@swift-5.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// swift-tools-version: 5.9
// swift-format-ignore-file

import CompilerPluginSupport
import Foundation
import PackageDescription
import CompilerPluginSupport

let package = Package(
name: "MetaCodable",
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ Supercharge `Swift`'s `Codable` implementations with macros.

## Overview

`MetaCodable` framework exposes custom macros which can be used to generate dynamic `Codable` implementations. The core of the framework is ``Codable()`` macro which generates the implementation aided by data provided with using other macros.
`MetaCodable` framework exposes custom macros which can be used to generate dynamic `Codable` implementations. The core of the framework is ``Codable(commonStrategies:)`` macro which generates the implementation aided by data provided with using other macros.

`MetaCodable` aims to supercharge your `Codable` implementations by providing these inbox features:

@@ -24,6 +24,7 @@ Supercharge `Swift`'s `Codable` implementations with macros.
- Allows to read data from additional fallback `CodingKey`s provided with ``CodedAs(_:_:)``.
- Allows to provide default value in case of decoding failures with ``Default(_:)``, or only in case of failures when missing value with ``Default(ifMissing:)``. Different default values can also be used for value missing and other errors respectively with ``Default(ifMissing:forErrors:)``.
- Allows to create custom decoding/encoding strategies with ``HelperCoder`` and using them with ``CodedBy(_:)``, ``CodedBy(_:properties:)`` or others. i.e. ``LossySequenceCoder`` etc.
- Allows applying common strategies like `ValueCoder` to all properties of a type through the ``Codable(commonStrategies:)`` parameter, reducing the need for repetitive property annotations.
- Allows specifying different case values with ``CodedAs(_:_:)`` and case value/protocol type identifier type different from `String` with ``CodedAs()``.
- Allows specifying enum-case/protocol type identifier path with ``CodedAt(_:)`` and case content path with ``ContentAt(_:_:)``.
- Allows decoding/encoding enums that lack distinct identifiers for each case data with ``UnTagged()``.
4 changes: 4 additions & 0 deletions Sources/HelperCoders/HelperCoders.docc/HelperCoders.md
Original file line number Diff line number Diff line change
@@ -75,3 +75,7 @@ Level up `MetaCodable`'s generated implementations with helpers assisting common
### Sequence

- ``SequenceCoder``

### Strategies

- ``HelperCoderStrategy``
21 changes: 21 additions & 0 deletions Sources/HelperCoders/Strategies/HelperCoderStrategy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import MetaCodable

/// An enumeration of supported helper coder strategies for use with the `@Codable` macro's `commonStrategies` parameter.
///
/// Use cases such as `.valueCoder()` allow you to specify that all properties should use a particular value coding strategy
/// (e.g., `ValueCoder`) for encoding and decoding, without annotating each property individually.
public enum HelperCoderStrategy {
/// Applies the `ValueCoder` strategy to all properties, optionally specifying additional types.
case valueCoder(_ additionalTypes: [any ValueCodingStrategy.Type] = [])
// Future cases can be added here
}

public extension CodableCommonStrategy {
/// Returns a `CodableCommonStrategy` representing the use of a helper coder strategy for all properties.
///
/// - Parameter helperCoderStrategy: The helper coder strategy to apply (e.g., `.valueCoder()`).
/// - Returns: A `CodableCommonStrategy` value for use in the `commonStrategies` parameter of `@Codable`.
static func codedBy(_ helperCoderStrategy: HelperCoderStrategy) -> Self {
return .init()
}

Check warning on line 20 in Sources/HelperCoders/Strategies/HelperCoderStrategy.swift

Codecov / codecov/patch

Sources/HelperCoders/Strategies/HelperCoderStrategy.swift#L18-L20

Added lines #L18 - L20 were not covered by tests
}
12 changes: 8 additions & 4 deletions Sources/MetaCodable/Codable/Codable.swift
Original file line number Diff line number Diff line change
@@ -46,6 +46,10 @@
/// * If attached declaration already conforms to `Codable` this macro expansion
/// is skipped.
///
/// - Parameters:
/// - commonStrategies: An array of CodableCommonStrategy values specifying
/// type conversion strategies to be automatically applied to all properties of the type.
///
/// - Important: The attached declaration must be of a `struct`, `class`, `enum`
/// or `actor` type. [See the limitations for this macro](<doc:Limitations>).
@attached(
@@ -58,17 +62,17 @@
names: named(CodingKeys), named(init(from:)), named(encode(to:))
)
@available(swift 5.9)
public macro Codable() =
public macro Codable(commonStrategies: [CodableCommonStrategy] = []) =
#externalMacro(module: "MacroPlugin", type: "Codable")

/// Indicates whether super class conforms to `Codable` or not.
///
/// By default, ``Codable()`` assumes class inherits `Decodable`
/// By default, ``Codable(commonStrategies:)`` assumes class inherits `Decodable`
/// or `Encodable` conformance if it doesn't receive protocol needs
/// to be conformed from the compiler. Using this macro, it can be explicitly
/// indicated that the class doesn't inherit conformance in such cases.
///
/// Following code indicates ``Codable()`` that `Item` class doesn't
/// Following code indicates ``Codable(commonStrategies:)`` that `Item` class doesn't
/// inherit conformance:
/// ```swift
/// @Codable
@@ -87,7 +91,7 @@ public macro Codable() =
/// - encodable: Whether super class conforms to `Encodable`.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a class declaration. ``Codable()`` macro uses this macro
/// is a class declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
@attached(peer)
@available(swift 5.9)
23 changes: 23 additions & 0 deletions Sources/MetaCodable/Codable/CodableCommonStrategy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// CodableCommonStrategy.swift
// Defines the CodableCommonStrategy struct for commonStrategies parameter in @Codable macro.

/// A marker type used to represent a common type conversion strategy for the `@Codable` macro.
///
/// `CodableCommonStrategy` is used as the element type for the `commonStrategies` parameter in the
/// `@Codable` macro. It allows users to specify strategies (such as value coding) that should be
/// automatically applied to all properties of a type, so that users do not have to annotate each property
/// individually. The macro system interprets these strategies and injects the appropriate coding logic
/// during macro expansion.
///
/// Example usage:
/// ```swift
/// @Codable(commonStrategies: [.codedBy(.valueCoder())])
/// struct MyModel {
/// let int: Int
/// let string: String
/// }
/// ```
public struct CodableCommonStrategy {
// Only allow MetaCodable to construct
package init() {}

Check warning on line 22 in Sources/MetaCodable/Codable/CodableCommonStrategy.swift

Codecov / codecov/patch

Sources/MetaCodable/Codable/CodableCommonStrategy.swift#L22

Added line #L22 was not covered by tests
}
6 changes: 3 additions & 3 deletions Sources/MetaCodable/Codable/CodingKeys.swift
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@
/// let description: String
/// }
/// ```
/// The ``Codable()`` macro generated code will transform field names
/// The ``Codable(commonStrategies:)`` macro generated code will transform field names
/// to snake-case in the `Codable` implementation.
///
/// Similarly, for enums associated value label can be kept camel-cased while
@@ -88,10 +88,10 @@
/// ``CodedAt(_:)`` will remain unchanged.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: This attribute must be used combined with ``Codable()``.
/// - Important: This attribute must be used combined with ``Codable(commonStrategies:)``.
///
/// [Swift API Design Guidelines]:
/// https://www.swift.org/documentation/api-design-guidelines/#general-conventions
4 changes: 2 additions & 2 deletions Sources/MetaCodable/Codable/IgnoreCodingInitialized.swift
Original file line number Diff line number Diff line change
@@ -57,10 +57,10 @@
/// while `notIgnored` is decoded and encoded.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: This attribute must be used combined with ``Codable()``.
/// - Important: This attribute must be used combined with ``Codable(commonStrategies:)``.
@attached(peer)
@available(swift 5.9)
public macro IgnoreCodingInitialized() =
2 changes: 1 addition & 1 deletion Sources/MetaCodable/Codable/UnTagged.swift
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@
/// variables are successfully decoded, is chosen as the variation case value.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: This macro can only be applied to enums.
6 changes: 3 additions & 3 deletions Sources/MetaCodable/CodedAs.swift
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@
/// - Parameter values: The values to use.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The value type must be `String` when used in
@@ -103,7 +103,7 @@ public macro CodedAs<T: Codable & Equatable>(_ values: T, _: T...) =
/// ```
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: For each case ``CodedAs(_:_:)`` macro with values
@@ -116,7 +116,7 @@ public macro CodedAs<T: Codable & Equatable>(_ values: T, _: T...) =
/// type must be same as the type defined with this macro, in absence of this macro
/// ``DynamicCodable/IdentifierValue`` type must be `String`.
///
/// - Important: This attribute must be used combined with ``Codable()``
/// - Important: This attribute must be used combined with ``Codable(commonStrategies:)``
/// and ``CodedAt(_:)``.
@attached(peer)
@available(swift 5.9)
2 changes: 1 addition & 1 deletion Sources/MetaCodable/CodedAt.swift
Original file line number Diff line number Diff line change
@@ -124,7 +124,7 @@
/// - Parameter path: The `CodingKey` path value located at.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: When applied to fields, the field type must confirm to
14 changes: 7 additions & 7 deletions Sources/MetaCodable/CodedBy.swift
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
/// - Parameter helper: The value that performs decoding and encoding.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The `helper`'s ``HelperCoder/Coded``
@@ -23,7 +23,7 @@
/// - Important: When using with enums and protocols if ``HelperCoder/Coded``
/// is other than `String` type must be provided with ``CodedAs()`` macro.
///
/// - Important: This attribute must be used combined with ``Codable()``
/// - Important: This attribute must be used combined with ``Codable(commonStrategies:)``
/// and ``CodedAt(_:)`` when applying to enums/protocols.
@attached(peer)
@available(swift 5.9)
@@ -73,7 +73,7 @@ public macro CodedBy<T: HelperCoder>(_ helper: T) =
/// - properties: The key path to properties passed to the creation action.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The `Parent` type must be the current `struct`/`class`/`actor`
@@ -139,7 +139,7 @@ public macro CodedBy<Parent, Helper: HelperCoder, each Property>(
/// - properties: The key path to properties passed to the creation action.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The `Parent` type must be the current `struct`/`class`/`actor`
@@ -166,7 +166,7 @@ public macro CodedBy<Parent, Helper: HelperCoder, each Argument, each Property>(
/// - properties: The key path to properties passed to the creation action.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The `Parent` type must be the current `struct`/`class`/`actor`
@@ -196,7 +196,7 @@ public macro CodedBy<Parent, Helper: HelperCoder, Argument1, each Property>(
/// - properties: The key path to properties passed to the creation action.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The `Parent` type must be the current `struct`/`class`/`actor`
@@ -229,7 +229,7 @@ public macro CodedBy<
/// - properties: The key path to properties passed to the creation action.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The `Parent` type must be the current `struct`/`class`/`actor`
2 changes: 1 addition & 1 deletion Sources/MetaCodable/CodedIn.swift
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@
/// - Parameter path: The `CodingKey` path of container value located in.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Note: Providing no arguments has the same effect as not applying
4 changes: 2 additions & 2 deletions Sources/MetaCodable/ContentAt.swift
Original file line number Diff line number Diff line change
@@ -47,10 +47,10 @@
/// protocol conforming type data located at.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: This attribute must be used combined with ``Codable()``
/// - Important: This attribute must be used combined with ``Codable(commonStrategies:)``
/// and ``CodedAt(_:)``.
@attached(peer)
@available(swift 5.9)
6 changes: 3 additions & 3 deletions Sources/MetaCodable/Default.swift
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
/// - Parameter default: The default value to use.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The field type must confirm to `Codable` and
@@ -47,7 +47,7 @@ public macro Default<T>(_ default: T) =
/// - Parameter default: The default value to use.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The field type must confirm to `Codable` and
@@ -80,7 +80,7 @@ public macro Default<T>(ifMissing default: T) =
/// - errorDefault: The default value to use for other errors.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The field type must confirm to `Codable` and
4 changes: 2 additions & 2 deletions Sources/MetaCodable/DynamicCodable/DynamicCodable.swift
Original file line number Diff line number Diff line change
@@ -5,10 +5,10 @@
///
/// Conforming to this `protocol` allows type to be decoded/encoded dynamically
/// if it conforms to any additional `protocol`(s) that declare dynamic
/// decoding/encoding with attached ``Codable()`` macro, while the conformed
/// decoding/encoding with attached ``Codable(commonStrategies:)`` macro, while the conformed
/// types can be declared in several different targets.
///
/// To use dynamic decoding, first declare a protocol with ``Codable()`` macro
/// To use dynamic decoding, first declare a protocol with ``Codable(commonStrategies:)`` macro
/// attached that represents common data. i.e. for dynamic `Post` data:
///
/// ``` json
10 changes: 5 additions & 5 deletions Sources/MetaCodable/IgnoreCoding.swift
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@
/// the data for `field` case or `Load` type respectively.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
@attached(peer)
@available(swift 5.9)
@@ -71,7 +71,7 @@ public macro IgnoreCoding() =
/// type will be encoded.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
@attached(peer)
@available(swift 5.9)
@@ -108,7 +108,7 @@ public macro IgnoreDecoding() =
/// type will be decoded if case related data is present.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
@attached(peer)
@available(swift 5.9)
@@ -145,7 +145,7 @@ public macro IgnoreEncoding() =
/// - Parameter condition: The condition to be checked.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The condition argument types must confirm to `Codable`
@@ -167,7 +167,7 @@ public macro IgnoreEncoding<each T>(if condition: (repeat each T) -> Bool) =
/// - Parameter condition: The condition to be checked.
///
/// - Note: This macro on its own only validates if attached declaration
/// is a variable declaration. ``Codable()`` macro uses this macro
/// is a variable declaration. ``Codable(commonStrategies:)`` macro uses this macro
/// when generating final implementations.
///
/// - Important: The field type must confirm to `Codable` and
Loading