Codable synthesis for Swift enums
- Tram Ho
One of the main advantages of Swift’s Codable API is how the compiler can automatically synthesize various encoding and decoding implementations when using it. In many cases, all we have to do to allow a Swift type to be serialized into formats like JSON is simply mark it as Codable
and the compiler will take care of the rest. Let’s see how that automatic aggregation works specifically for enums, and how part of the system has been upgraded in Swift 5.5.
Raw representable enums
Enums typically come in two variants – those supported by raw values (such as Int
or String
) and variants containing associated values. Since the introduction of Codable in Swift 4.0, enums of the old type have always supported compiler aggregation. So, for example, let’s say that we are working on an application that includes enums backed by String
hereafter, obey Codable
:
1 2 3 4 5 6 | enum MediaType: String, Codable { case article case podcast case video } |
Since the compiler can automatically synthesize all the code needed to encode and decode enums with raw values, we usually don’t have to write any extra code beyond that – which means we now have You can freely use the above enum in the Codable
types else, like this:
1 2 3 4 5 6 | struct Item: Codable { var title: String var url: URL var mediaType: MediaType } |
If we now encode an instance of Item
type above to JSON, then we will get the following output (because the values MediaType
will automatically be encoded and decoded using the raw String
values support them): { “title”: “Swift by Sundell”, “url”: “https://swiftbysundell.com/podcast“, “mediaType”: “podcast” } But what if instead we wanted to encode or decode an enum that supported associated values? Let’s look at that next.
Associated values
Before Swift 5.5, if we want to create an enum containing associated values that match Codable
, then we have to write all that code manually. However, that no longer happens, as the compiler has received an upgrade that makes it capable of auto-synthesizing the serialization code for such enums. Example: enum Video
the following can now be created Codable
without requiring any custom code on our part:
1 2 3 4 5 6 | enum Video: Codable { case youTube(id: String) case vimeo(id: String) case hosted(url: URL) } |
To see what the above type looks like when encoded, create an instance of VideoCollection
store an array of values Video
:
1 2 3 4 5 6 7 8 9 10 11 12 13 | struct VideoCollection: Codable { var name: String var videos: [Video] } let collection = VideoCollection( name: "Conference talks", videos: [ .youTube(id: "ujOc3a7Hav0"), .vimeo(id: "234961067") ] ) |
If we then encode the set value above to JSON, we get the following output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | { "name": "Conference talks", "videos": [ { "youTube": { "id": "ujOc3a7Hav0" } }, { "vimeo": { "id": "234961067" } } ] } |
So, by default, when we let the compiler auto-synthesize the Codable for an enum with associated values, the names of our instances and the labels for the associated values in them will be used when calculating the quantification. serialized form of that type.
Key customization
Just like when working with structs and classes, we can customize which keys should be used when encoding or decoding enum’s cases and associated values, and we can even use that ability to completely ignore some. certain case. For example, suppose we want to expand Video
enum to add support for local videos, but there’s no logical way for us to serialize the data for those videos. While we can always create a completely separate type for presenting such videos, we can also use enum CodingKeys
nested to ask the compiler to ignore local
case when creating implementation Video
type’s Codable
. As follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | enum Video: Codable { case youTube(id: String) case vimeo(id: String) case hosted(url: URL) case local(LocalVideo) } extension Video { enum CodingKeys: String, CodingKey { case youTube case vimeo case hosted = "custom" } } |
If needed, we can even customize which key is used for the associated values in a particular case. For example, this is how we can declare that we want the value id
of the YouTube
case is serialized as youTubeID
:
1 2 3 4 5 6 | extension Video { enum YouTubeCodingKeys: String, CodingKey { case id = "youTubeID" } } |
Enum YouTubeCodingKeys
the above matches YouTube
case by name, so if we also want to customize vimeo
case, then we can add an enum VimeoCodingKeys
for that case.
Conclusion
While Codable’s auto-aggregation has its limitations (especially when working with serialized formats that are vastly different from how we like to organize data in our Swift types) , it’s incredibly convenient – and often especially useful when working with locally stored values, such as when caching values on disk or when reading bundled configuration files. Regardless, the fact that enums with associated values can now join the auto-synthesis party is certainly a good thing in terms of consistency and will prove to be quite useful in a variety of code bases – including both my own code bases.
Hope the article will be useful to you.
Reference: https://www.swiftbysundell.com/articles/codable-synthesis-for-swift-enums/