Excluding properties from automatic Codable

Automatic Codable synthesis doesn't have a nice way to exclude just one or two properties. It appears you have to override an enum and list the ones you want to encode/decode yourself, but that's pretty much the opposite of what I wanted since I ran into this when I just wanted to exclude a single delegate property from serialization but keep all of the other properties. Maintaining that enum manually in a case like this would be a recipe for disaster, IMO. Ideally I could just annotate the property somehow (@skipCodable)?

2 Likes

There was talk during the pitch/review for automatic conformance to Codable (and Equatable and Hashable) that we could introduce a “@transient” attribute whereby developers can mark properties to exclude from automatic synthesis.

The discussion got bogged down in details about whether it should be possible to make properties transient with respect to code generation for some protocols but not others, and the decision was made to separate out that attribute from the automatic synthesis features.

Personally, I think @transient is well worth including, and I’d support it being just one attribute that applies to all automatic synthesis. If someone wants more customization than that, then they can manually implement the protocol requirements.

For the common case, simply being able to mark a cache or delegate as @transient would be quite beneficial.

In general here, even, there are a whole host of properties that would be useful to indicate beyond just @transient, like specifying some adaptors to more easily override the encoding/decoding behavior of specific properties without overriding all of init(from:)/encode(to:).

The full design that we would want in this space is closer to the original Property Behaviors proposal which was ultimately deferred because of lack of time/complexity of implementation. There's a lot that can be done here with macros too, but that space is also not yet developed.

In the interest of keeping options open, the current design minimized the addition of new annotations like this in favor of flexibility of design in the future. There's definitely a lot of quality-of-life improvements that we're looking to make here (especially surrounding overriding behavior for only one or two properties without losing the rest of synthesis), but those are at least partially influenced by the lack of these other features at the moment.

2 Likes

Does the lack of those other features mean these sorts of quality of life improvements to this feature are simply on hold indefinitely?

I wouldn't say that — we can always choose to make QoL improvements here and now. I do, however, think that there's something to be gained by not coming up with one-off solutions and taking the time to notice patterns from around Swift and how we could make the biggest impact by designing something that would solve them all at once.

If we decided to introduce @transient now, for instance, there would be some amount of complexity in dealing with that abstraction vs. CodingKeys — for instance, if you mark a property @transient but explicitly give it in CodingKeys, what wins out? Or is that an error? (Whatever decision we make, there's some complexity in that.)

If, however, we were to wait until we had a better answer for adaptors as well, and a consistent syntax to use, it would be significantly easier to, say, deprecate CodingKeys as the structure that guides synthesis, and delegate to annotations/property behaviors/what-have-you instead.

As in everything, there's a balance to strike, and the question is what we would prefer to do to end up making the biggest impact in the end vs. potentially making a lot of small changes along the way.

2 Likes

Thanks! I know there's value in not going with a knee-jerk fix for this kind of thing, of course. I figured it would help to post the issues I've run into recently as I worked more with Swift - which is why I posted this thread (and a couple others today) in the first place. I don't know if there's a generally better way to register "quality of life" kinds of commentary or not, I guess.

Long-term, when/if auto-synthesis is replaced with more general code generation/reflection, I’d love for Swift to gain custom attribute declarations roughly analogous to attributes in Rust, Java and other languages. In the case of Codable, these attributes could signify things like:

  • exclusion from decoding/encoding
  • a custom coding key
  • probably other things

In the short-term, some of these attributes (such as @transient) could be introduced as built-ins.

I’m not sure that property behaviours really fit this use case, but maybe I’m thinking about them incorrectly.

2 Likes

This does cause edge cases (for instance, what value is the transient property initialized to in the generated initializer?)

Speaking as one who does not have authority on these sorts of things, I find a lot of the behavioral attributes for serialization to be frustrating in other languages. You hardly ever can create custom attributes, and when the available values fail you have to resort to the (often under-baked) custom serializer API.

I kinda prefer just shuttling all custom behavior to 'go ahead and implement Codable yourself then', and focusing on making those implementations terse and safe.