I recently had a situation where being able to attach peer macros to enum case declarations would've been a boon towards removing custom initializers. Something like this:
@APIResponsePayload
enum MyResponsePayload {
@Attributes(statusCode: 200)
case success(MyPayload)
@Attributes(statusCode: 404)
case notFound
}
expanding to this:
enum MyResponsePayload: APIResponsePayload {
case success(MyPayload)
case notFound
init(from decoder: any Decoder) throws {
guard let urlResponse = decoder.userInfo[Self.urlResponseUserInfoKey] as? HTTPURLResponse else {
throw APIResponsePayloadDecoderError.notHTTPURLResponse
}
switch urlResponse.statusCode {
case 200:
self = .success(try .init(from: decoder))
case 404:
self = .notFound
default:
throw APIResponsePayloadDecoderError.unknownResponse
}
}
}
This unfortunately doesn't seem possible today as enum case declarations don't support attached macros. Is there a specific reason why this was omitted, or is this just work that hasn't been prioritized yet?
This doesn’t really fit the use-case we’re going after, which is attaching metadata to individual enum cases for use in other methods (e.g. decoding). In my specific use-case, I’m trying to streamline the mapping of HTTP status codes (and maybe other attributes, like the Content-Type header) to the appropriate type-safe enum case.
For example, longer term, I’d love to be able to version a /user endpoint and have this be the extent of the additional parsing code:
@APIResponsePayload
enum UserAPIResponsePayload {
@Attributes(statusCode: 200, contentType: "application/vnd.my-app.user.v1")
case successV1(UserPayloadV1)
@Attributes(statusCode: 200, contentType: "application/vnd.my-app.user.v2")
case successV2(UserPayloadV2)
@Attributes(statusCode: 404)
case notFound
}
Here, these attached attributes are used to select the case (based on response metadata like status code/headers/other); they’re not part of the case’s associated values.
Beyond my own use-case, this kind of per-case metadata could be useful for things like analytics tagging, routing hints, and other custom synthesis behaviors. The common theme is attaching structured, compile-time metadata to individual enum cases for use elsewhere; not modeling additional runtime data in the payload itself.
You can declare @Attributes in your example as "marker" peer macro which expands to nothing, and generate all the code you need in your @APIResponsePayload implementation.
I don’t recall what errors I ran into when I first tried this several months ago, but it may have just been user error, as spinning up a fresh example works as expected now.