Hello, everyone! As part of the review thread for SE-0239, we received a lot of helpful feedback that's highlighted areas where we thing Codable can improve, and we wanted to take the time after the holidays to split that conversation aside and help carry it along in a more targeted thread. We think there are a lot of potential improvements to be made, and we're really interested in getting community feedback and contributions to help us get closer to where we've always wanted Codable to be.
As part of this, I wanted to gather some of my own thoughts about various improvements that can be made, to help seed some discussion about priorities, and what we'd like to see happen in the next few releases:
-
[Compiler- & Library-Level] Per-Property Refinement of Encoding/Decoding without Abandoning Synthesis: This is probably one of the larger topics to discuss, and potentially one of the more impactful ones. One of the more helpful aspects of the Codable feature is the compiler synthesis of
init(from:)
andencode(to:)
, and likewise, one of the more painful aspects is the loss of synthesis due to the need to customize behavior for just one or two properties, out of, say, 19. I've seen developers use Sourcery or similar to produce the equivalent of what the compiler would synthesize, and then use that as a starting point for customization; of course, this is sub-optimal, and it would be nice to be able to address this.I've discussed this briefly in other threads (largely under the description of Codable "adaptors"), but to re-cap some potential directions to take this in:
-
Compiler-heavy solution: some form of property-level annotations that indicate how to synthesize
init(from:)
andencode(to:)
by adjusting how the property is assigned to during encoding/decoding. This could take the form of something similar to SE-0030 Property Behaviors (which was discussed but deferred), or actual user-level annotations (e.g.@codableAdaptor(...) var myProperty: Foo
). This subject has been discussed at length, but I'd like to note that it's unlikely we'd want to implement a feature like this just for Codable, so this direction would likely require revisiting this topic and re-generalizing it -
Library-heavy solution: some way to describe, via, say, a
static func
, what adaptor to use for a givenCodable
property. Adaptors would likely just be functions, and you can imagine this conceptually as just being a mapping fromproperty -> adaptor
. Difficulties here lie in representing this in the type system without higher-kinded-types, or more generalized generic functions: because the properties on a typeT
can have all different types themselves (Int
andString
andDate
and...), there is no way to generalize over all of the adaptors you'd want to use onT
without losing type information. This means storage in an actual[KeyPath : Adaptor]
is likely out, so some creativity in how to perform this mapping cleanly might be helpful. It's likely that we'll need additional compiler support to get this to work - Tooling-heavy solition: it's also possible (though perhaps less desirable) to punt this in favor of having the language provide easier support for simply doing what developers are doing now: allow the compiler to export the synthesized implementations themselves so Xcode can splat that into your file (rather than going through, say, Sourcery). [Internal: I filed 30472233 a while back for this]
- Something else? There are plenty of directions this can go in, so creative feedback is appreciated
-
Compiler-heavy solution: some form of property-level annotations that indicate how to synthesize
-
[Library-Level] Enhanced Contextual Overriding of Encoded Representations: Some of this already exists in the form of encoding and decoding strategies, which exist on the
Encoder
/Decoder
level. It's possible to expand these strategies to cover more types, or to expand strategies to apply replacements for all types on an archive-wide level. There's also room here for potential language enhancements to somehow inject scoped replacements for various types without needingEncoder
/Decoder
involvement, but that hasn't been sketched out or explored much.There's room here for exploration of what we consider reasonable and principled, while making pragmatic decisions about how to encode values. (Originally, we decided against exposing strategies that allowed replacement of all types for the benefit of encapsulation, but perhaps that decision should be revisited.)
-
[Library-Level] Encoder/Decoder Exposition of Capabilities: This can cover a lot of little improvements, but the gist here is to find a way to expose format-level information on
Encoder
s andDecoder
s that can help provide even more context for how types can encode.We already provide
userInfo
andcodingPath
to help pass along information to a given type (userInfo
is helpful in passing along context that theEncoder
/Decoder
doesn't know about;codingPath
is helpful for telling where in a payload you currently are); we can imagine an additional.capabilities
property or similar which exposes information about theEncoder
/Decoder
itself — what format it is encoding to, whether it supports certain things like reference semantics (which is a concept we'd originally included in the Codable library support, but had to drop for time), and whether it prefers certain representations (e.g. a minimal representation over a human-readable one, or vice versa).There's room here for a grab bag of improvements here that we might be able to gather in one spot, and I'm happy to elaborate on thoughts I've had here.
-
Others: of course, there's a lot more that can be proposed and done, but I'd like to offer these as starter ideas for where we can make improvements. If some of the bigger ideas solidify, we can also take discssion of those into separate threads as well to target specific concepts.
We'd love to get further feedback on limitations and pain points that you would find helpful in addressing, and more so, would find thoughts about how to address those points extremely helpful in providing targeted solutions. Thanks!