Sure, I'll describe this a bit more.
There are two main purposes for serializing values:
- Serializing a value so another system can operate on the serialized form later
- Serializing a value so I can operate on the serialized form later
JSON, CSV, XML, ProtoBuf, etc all tend to be the first form. They're "currency formats" that are commonly used across a wide array of applications, so they serve as nicely-introspectable ways to communicate some structured information back and forth.
The other main scenario is more like "I'm going to hand this value to a framework so that it can hand it back to me later". In this case, the framework wants an opaque blob, because both the provider of the blob and the receiver of the blob are, if not the same application, at least both well known to each other and the framework is operating as a transport or storage layer.
NSKeyedArchiver
was a nice way to get #2, because we could hand it values and say "encode this please" and we'd get an NSData
out the other side that we could store and save and deserialize later. We couldn't do a whole lot with it, but that was also the point. If we got something that was NSCoding
, we knew that it could be transported or persisted, and we could use protocols to extract semantic information from it.
I bring this up because Codable
tried to be both. It tried to be an API to serialize to common formats (and while mostly successful, it had a myriad of Sharpe edges) as well as the way to describe an arbitrarily-serializable type.
In my experience, I've found that conflating these two kinds of serialization tends to make things overly complicated. If I'm writing a framework, I either want to receive values that I turn into blobs for storage, or I extract information from them via protocols. If I'm writing an app, I'll mostly care about the transport format of my types because the server on the other end of the wires wants a particular format.
Trying to shoehorn the two goals into the same API has always been pretty complicated from a client's side. Either I'm adopting a super abstract API when I really care about a specific format, or I'm trying to twist a specific API into supporting all kinds of abstract use-cases. That's why I'm suggesting that we split the API to support the cases separately. We have one API that can be very general and support the whole "A type can be serialized to an opaque format" use-case, and then packages to support particular formats and all of their respective idiosyncrasies. I think we'd be repeating past mistakes to try and make those two use cases be the same API again.