The future of serialization & deserialization APIs

Another issue I've run into with Codable is that a given object may have more than one serialized representation in a given application.

Here's a (slightly contrived) example: Suppose I am writing something like a Mastodon client:

I have a Post model. When I retrieve this from the server, I decode it using JSONDecoder according to the server API scheme. I then save a copy to disc using the same scheme.

Later, Mastodon changes their API and so I update my decoding logic, however I still need to support my locally saved data which uses the old format.

Later still, I update my client to also support Bluesky, but I have a problem because Bluesky posts, while they share a similar structure to Mastodon posts, use a different scheme. So now I want to decode the same object using three different JSON schemes.

The way to solve this in Swift at the moment is simply to use separate "Data Transport Objects" for each scheme - so you'd have a MastodonPostDTO struct and a BlueSkyPostDTO struct and you'd decode these and then use them to initialize the Post constructor.

And that's fine, except there's a tonne of boilerplate involved in initializing one object from another - you have to manually copy over every property, and none of that code can be synthesized. It's easy to make a typo or miss an optional property.

The other problem is that we're not usually talking about decoding one object, but a whole object graph. And so to map an entire tree of objects to another tree involves even more boilerplate, with endless dtoPostArray.map { Post($0) } code that is potentially full of bugs and (presumably) non-optimal from a performance or memory usage standpoint.

It would be much cleaner if the encoding scheme for a given object and encoder was a conceptually separate piece, so that you could just write something like:

JSONDecoder.decode([Post].self, from: jsonData, with: BlueskyDecodingScheme.self)

And the decoder would take care of all the mapping (and doing so in an optimal way in terms of reducing copies, etc).

EDIT: this would also solve another (probably more common) problem, which is wanting to retrofit existing Foundation or UIKit types that don't already have it with Codable support, or wanting to encode them using a different scheme than the one Apple decided on.

20 Likes