I have a Swift struct
that looks like this:
struct MyStruct: Codable {
var id: String
var name: String
var createdDate: Date
}
To that, I would like to add another property: a [String:Any]
dictionary. The result would look like this:
struct MyStruct: Codable {
var id: String
var name: String
var createdDate: Date
var attributes: [String:Any] = [:]
}
In the end, I would like to be able to serialize my MyStruct
instance to a JSON string, and vice versa. However, when I go to build I get an error saying,
Type 'MyStruct' does not conform to protocol 'Codable'
Type 'MyStruct' does not conform to protocol 'Decodable'
It's clearly the attributes
var
that is tripping up my build, but I'm not sure how I can get the desired results. Any idea how I can code my struct
to support this?
Since the comments already point out that
Any
type has nothing to do with generics, let me jump straight into the solution.First thing you need is some kind of wrapper type for your Any attribute values. Enums with associated values are great for that job. Since you know best what types are to be expected as an attribute, feel free to add/remove any case from my sample implementation.
We will be later wrapping attribute values from the
[String: Any]
dictionary into the wrapper enum cases, but first we need to make the type conform to theCodable
protocols. I am usingsingleValueContainer()
for the decoding/encoding so the final json will produce a regular json dicts.At this point we are good to go, but before we will decode/encode the attributes, we can use some extra interoperability between
[String: Any]
and[String: MyAttrubuteValue]
types. To map easily betweenAny
andMyAttrubuteValue
lets add the following:Now, with the quick
value
access and newinit
, we can map values easily. We are also making sure that the helper properties are only available for the dictionaries of concrete types, the ones we are working with.Now the final part, a custom
Codable
implementation forMyStruct
This solution is fairly long, but pretty straight-forward at the same time. We lose automatic
Codable
implementation, but we got exactly what we wanted. Now you are able to encode ~Any~ type that already conforms toCodable
easily, by adding an extra case to your newMyAttrubuteValue
enum. One final thing to say is that we use similar approach to this one in production, and we have been happy so far.That's a lot of code, here is a gist.