Skip to content

Commit 83ea43a

Browse files
authored
feat: added ignoring encoding based on condition over value (SwiftyLab#84)
1 parent 69cc0de commit 83ea43a

35 files changed

Lines changed: 597 additions & 81 deletions

.github/config/spellcheck-wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,4 @@ mergeBehavior
6464
lowercasing
6565
SwiftData
6666
SwiftUI
67+
IgnoreEncoding

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Supercharge `Swift`'s `Codable` implementations with macros.
2727
- Allows specifying different case values with ``CodedAs(_:_:)`` and case value/protocol type identifier type different from `String` with ``CodedAs()``.
2828
- Allows specifying enum-case/protocol type identifier path with ``CodedAt(_:)`` and case content path with ``ContentAt(_:_:)``.
2929
- Allows decoding/encoding enums that lack distinct identifiers for each case data with ``UnTagged()``.
30-
- Allows to ignore specific properties/cases from decoding/encoding with ``IgnoreCoding()``, ``IgnoreDecoding()`` and ``IgnoreEncoding()``.
30+
- Allows to ignore specific properties/cases from decoding/encoding with ``IgnoreCoding()``, ``IgnoreDecoding()`` and ``IgnoreEncoding()``. Allows to ignore encoding based on custom conditions with ``IgnoreEncoding(if:)``.
3131
- Allows to use camel-case names for variables according to [Swift API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/#general-conventions), while enabling a type/case to work with different case style keys with ``CodingKeys(_:)``.
3232
- Allows to ignore all initialized properties of a type/case from decoding/encoding with ``IgnoreCodingInitialized()`` unless explicitly asked to decode/encode by attaching any coding attributes, i.e. ``CodedIn(_:)``, ``CodedAt(_:)``, ``CodedBy(_:)``, ``Default(_:)`` etc.
3333
- Allows to generate protocol decoding/encoding ``HelperCoder``s with `MetaProtocolCodable` build tool plugin from ``DynamicCodable`` types.

Sources/MetaCodable/Default.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,7 @@ public macro Default<T>(ifMissing default: T) =
8787
/// default value type `T` must be the same as field type.
8888
@attached(peer)
8989
@available(swift 5.9)
90-
public macro Default<T>(ifMissing missingDefault: T, forErrors errorDefault: T) =
90+
public macro Default<T>(
91+
ifMissing missingDefault: T, forErrors errorDefault: T
92+
) =
9193
#externalMacro(module: "MacroPlugin", type: "Default")

Sources/MetaCodable/IgnoreCoding.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,65 @@ public macro IgnoreDecoding() =
114114
@available(swift 5.9)
115115
public macro IgnoreEncoding() =
116116
#externalMacro(module: "MacroPlugin", type: "IgnoreEncoding")
117+
118+
/// Indicates the field/case needs to be encoded only if provided condition
119+
/// is satisfied.
120+
///
121+
/// This macro can be applied to variables to ignore them from encoding.
122+
/// ```swift
123+
/// @IgnoreEncoding(if: \String.isEmpty)
124+
/// let field: String
125+
/// ```
126+
///
127+
/// The decoding data needs to have applicable data in `field` key.
128+
/// But the encoded data might not have any `field` key for specific values
129+
/// if the condition for those values return `false`.
130+
///
131+
/// Similarly, for enums this macro can be applied to cases
132+
/// to ignore them from encoding.
133+
/// ```swift
134+
/// func fieldEncodable(_ str: String) {
135+
/// return !str.isEmpty
136+
/// }
137+
///
138+
/// @IgnoreEncoding(if: fieldEncodable)
139+
/// case field(String)
140+
/// ```
141+
///
142+
/// This case will never be encoded. But `field` case will be decoded
143+
/// if case related data is present.
144+
///
145+
/// - Parameter condition: The condition to be checked.
146+
///
147+
/// - Note: This macro on its own only validates if attached declaration
148+
/// is a variable declaration. ``Codable()`` macro uses this macro
149+
/// when generating final implementations.
150+
///
151+
/// - Important: The condition argument types must confirm to `Codable`
152+
/// and the single argument should match attached type when attached to field.
153+
/// When attached to cases the arguments count, order and types should match
154+
/// attached enum-case associated variables.
155+
@attached(peer)
156+
@available(swift 5.9)
157+
public macro IgnoreEncoding<each T>(if condition: (repeat each T) -> Bool) =
158+
#externalMacro(module: "MacroPlugin", type: "IgnoreEncoding")
159+
160+
/// Indicates the field needs to be encoded only if provided condition
161+
/// is satisfied.
162+
///
163+
/// Provides same functionality as ``IgnoreEncoding(if:)-1iuvv``
164+
/// for fields, provided as separate macro to allow usage in case of
165+
/// Swift parameter packs feature isn't available.
166+
///
167+
/// - Parameter condition: The condition to be checked.
168+
///
169+
/// - Note: This macro on its own only validates if attached declaration
170+
/// is a variable declaration. ``Codable()`` macro uses this macro
171+
/// when generating final implementations.
172+
///
173+
/// - Important: The field type must confirm to `Codable` and
174+
/// default value type `T` must be the same as field type.
175+
@attached(peer)
176+
@available(swift 5.9)
177+
public macro IgnoreEncoding<T>(if condition: (T) -> Bool) =
178+
#externalMacro(module: "MacroPlugin", type: "IgnoreEncoding")

Sources/MetaCodable/MetaCodable.docc/Limitations.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,7 @@ Currently Swift Package Manager always returns empty list for Xcode target depen
8282
Currently during certain customization in SwiftUI, compiler is sending no protocol data to ``Codable()``. Due to this, ``Codable()`` tries to find `Codable` protocol implementation for the class. If no implementation found, ``Codable()`` assumes class inherits conformance from super class, and generates implementation accordingly causing issues like [#56](https://github.com/SwiftyLab/MetaCodable/issues/56).
8383

8484
Until this is fixes from Swift compiler, ``Inherits(decodable:encodable:)`` macro can be used to indicate explicitly that class doesn't inherit `Codable` conformance.
85+
86+
### Why IgnoreEncoding(if:) not supported for types and MetaProtocolCodable plugin?
87+
88+
As of writing, Swift has an [existing bug](https://github.com/apple/swift/issues/68158) which causes error when ``IgnoreEncoding(if:)-7toka`` or ``IgnoreEncoding(if:)-1iuvv`` is attached to a type declaration. [This bug is also being discussed](https://forums.swift.org/t/circular-reference-error-when-using-keypaths-to-properties-with-macros-bug-or-expected-behaviour/69162), and once fixed this feature will be supported by `MetaProtocolCodable` plugin as well.

Sources/MetaCodable/MetaCodable.docc/MetaCodable.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Supercharge `Swift`'s `Codable` implementations with macros.
2222
- Allows specifying different case values with ``CodedAs(_:_:)`` and case value/protocol type identifier type different from `String` with ``CodedAs()``.
2323
- Allows specifying enum-case/protocol type identifier path with ``CodedAt(_:)`` and case content path with ``ContentAt(_:_:)``.
2424
- Allows decoding/encoding enums that lack distinct identifiers for each case data with ``UnTagged()``.
25-
- Allows to ignore specific properties/cases from decoding/encoding with ``IgnoreCoding()``, ``IgnoreDecoding()`` and ``IgnoreEncoding()``.
25+
- Allows to ignore specific properties/cases from decoding/encoding with ``IgnoreCoding()``, ``IgnoreDecoding()`` and ``IgnoreEncoding()``. Allows to ignore encoding based on custom conditions with ``IgnoreEncoding(if:)-1iuvv`` and ``IgnoreEncoding(if:)-7toka``.
2626
- Allows to use camel-case names for variables according to [Swift API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/#general-conventions), while enabling a type/case to work with different case style keys with ``CodingKeys(_:)``.
2727
- Allows to ignore all initialized properties of a type/case from decoding/encoding with ``IgnoreCodingInitialized()`` unless explicitly asked to decode/encode by attaching any coding attributes, i.e. ``CodedIn(_:)``, ``CodedAt(_:)``, ``CodedBy(_:)``, ``Default(_:)`` etc.
2828
- Allows to generate protocol decoding/encoding ``HelperCoder``s with `MetaProtocolCodable` build tool plugin from ``DynamicCodable`` types.
@@ -86,6 +86,8 @@ Supercharge `Swift`'s `Codable` implementations with macros.
8686
- ``IgnoreCoding()``
8787
- ``IgnoreDecoding()``
8888
- ``IgnoreEncoding()``
89+
- ``IgnoreEncoding(if:)-1iuvv``
90+
- ``IgnoreEncoding(if:)-7toka``
8991
- ``CodingKeys(_:)``
9092
- ``IgnoreCodingInitialized()``
9193
- ``Inherits(decodable:encodable:)``

Sources/MetaCodable/MetaCodable.docc/Tutorials/Dynamic/Code/DynamicPost-05.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ protocol Post {
55
var id: UUID { get }
66
}
77

8+
typealias Identifier =
9+
DynamicCodableIdentifier<String>
10+
811
@Codable
912
struct TextPost: Post, DynamicCodable {
10-
static var identifier:
11-
DynamicCodableIdentifier<String> {
13+
static var identifier: Identifier {
1214
return "text"
1315
}
1416

@@ -18,8 +20,7 @@ struct TextPost: Post, DynamicCodable {
1820

1921
@Codable
2022
struct PicturePost: Post, DynamicCodable {
21-
static var identifier:
22-
DynamicCodableIdentifier<String> {
23+
static var identifier: Identifier {
2324
return "picture"
2425
}
2526

@@ -30,8 +31,7 @@ struct PicturePost: Post, DynamicCodable {
3031

3132
@Codable
3233
struct AudioPost: Post, DynamicCodable {
33-
static var identifier:
34-
DynamicCodableIdentifier<String> {
34+
static var identifier: Identifier {
3535
return "audio"
3636
}
3737

@@ -42,8 +42,7 @@ struct AudioPost: Post, DynamicCodable {
4242

4343
@Codable
4444
struct VideoPost: Post, DynamicCodable {
45-
static var identifier:
46-
DynamicCodableIdentifier<String> {
45+
static var identifier: Identifier {
4746
return "video"
4847
}
4948

Sources/MetaCodable/MetaCodable.docc/Tutorials/Dynamic/Code/DynamicPost-06.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ protocol Post {
55
var id: UUID { get }
66
}
77

8+
typealias Identifier =
9+
DynamicCodableIdentifier<String>
10+
811
@Codable
912
struct TextPost: Post, DynamicCodable {
10-
static var identifier:
11-
DynamicCodableIdentifier<String> {
13+
static var identifier: Identifier {
1214
return "text"
1315
}
1416

@@ -18,8 +20,7 @@ struct TextPost: Post, DynamicCodable {
1820

1921
@Codable
2022
struct PicturePost: Post, DynamicCodable {
21-
static var identifier:
22-
DynamicCodableIdentifier<String> {
23+
static var identifier: Identifier {
2324
return ["picture", "photo"]
2425
}
2526

@@ -30,8 +31,7 @@ struct PicturePost: Post, DynamicCodable {
3031

3132
@Codable
3233
struct AudioPost: Post, DynamicCodable {
33-
static var identifier:
34-
DynamicCodableIdentifier<String> {
34+
static var identifier: Identifier {
3535
return "audio"
3636
}
3737

@@ -42,8 +42,7 @@ struct AudioPost: Post, DynamicCodable {
4242

4343
@Codable
4444
struct VideoPost: Post, DynamicCodable {
45-
static var identifier:
46-
DynamicCodableIdentifier<String> {
45+
static var identifier: Identifier {
4746
return "video"
4847
}
4948

Sources/MetaCodable/MetaCodable.docc/Tutorials/Dynamic/Code/DynamicPost-07.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ protocol Post {
55
var id: UUID { get }
66
}
77

8+
typealias Identifier =
9+
DynamicCodableIdentifier<String>
10+
811
@IgnoreCoding
912
struct InvalidPost: Post {
1013
let id: UUID
@@ -13,8 +16,7 @@ struct InvalidPost: Post {
1316

1417
@Codable
1518
struct TextPost: Post, DynamicCodable {
16-
static var identifier:
17-
DynamicCodableIdentifier<String> {
19+
static var identifier: Identifier {
1820
return "text"
1921
}
2022

@@ -24,8 +26,7 @@ struct TextPost: Post, DynamicCodable {
2426

2527
@Codable
2628
struct PicturePost: Post, DynamicCodable {
27-
static var identifier:
28-
DynamicCodableIdentifier<String> {
29+
static var identifier: Identifier {
2930
return ["picture", "photo"]
3031
}
3132

@@ -36,8 +37,7 @@ struct PicturePost: Post, DynamicCodable {
3637

3738
@Codable
3839
struct AudioPost: Post, DynamicCodable {
39-
static var identifier:
40-
DynamicCodableIdentifier<String> {
40+
static var identifier: Identifier {
4141
return "audio"
4242
}
4343

@@ -48,8 +48,7 @@ struct AudioPost: Post, DynamicCodable {
4848

4949
@Codable
5050
struct VideoPost: Post, DynamicCodable {
51-
static var identifier:
52-
DynamicCodableIdentifier<String> {
51+
static var identifier: Identifier {
5352
return "video"
5453
}
5554

Sources/MetaCodable/MetaCodable.docc/Tutorials/Dynamic/Code/DynamicPost-08.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ protocol Post {
66
var id: UUID { get }
77
}
88

9+
typealias Identifier =
10+
DynamicCodableIdentifier<String>
11+
912
@IgnoreCoding
1013
struct InvalidPost: Post {
1114
let id: UUID
@@ -14,8 +17,7 @@ struct InvalidPost: Post {
1417

1518
@Codable
1619
struct TextPost: Post, DynamicCodable {
17-
static var identifier:
18-
DynamicCodableIdentifier<String> {
20+
static var identifier: Identifier {
1921
return "text"
2022
}
2123

@@ -25,8 +27,7 @@ struct TextPost: Post, DynamicCodable {
2527

2628
@Codable
2729
struct PicturePost: Post, DynamicCodable {
28-
static var identifier:
29-
DynamicCodableIdentifier<String> {
30+
static var identifier: Identifier {
3031
return ["picture", "photo"]
3132
}
3233

@@ -37,8 +38,7 @@ struct PicturePost: Post, DynamicCodable {
3738

3839
@Codable
3940
struct AudioPost: Post, DynamicCodable {
40-
static var identifier:
41-
DynamicCodableIdentifier<String> {
41+
static var identifier: Identifier {
4242
return "audio"
4343
}
4444

@@ -49,8 +49,7 @@ struct AudioPost: Post, DynamicCodable {
4949

5050
@Codable
5151
struct VideoPost: Post, DynamicCodable {
52-
static var identifier:
53-
DynamicCodableIdentifier<String> {
52+
static var identifier: Identifier {
5453
return "video"
5554
}
5655

0 commit comments

Comments
 (0)