How to customize automatic synthesizing Codable for enums with associated values
Table of Contents
Part 2 in a series on "Codable synthesis for enums with associated values". In the first part, we learn how easy it is to make enums with associated values conform to Codable protocol. This article will explore how much we can customize synthesized code to fit our needs.
Synthesized code
To customize default behavior, we must first understand what happened behind the scene.
We will use the same Role
enum from our previous example.
enum Role: Codable {
case admin
case guest(String?)
case member(id: String)
case vipMember(id: String, Int)
}
We learned from the previous article that it will encoded into a nested structure like this.
{
"admin" : {}
}
{
"guest" : {
"_0" : "John"
}
}
{
"member" : {
"id" : "1234"
}
}
{
"vipMember" : {
"id" : "1234",
"_1" : 5
}
}
I think it is easier to think of it in the form of a struct where each case is another struct with associated values as properties.
Here are similar structs which can represent the same JSON structure. This is for demonstration purposes only. It is not how synthesis work.
struct AdminStruct: Codable {}
struct GuestStruct: Codable {
let _0: String?
}
struct MemberStruct: Codable {
let id: String
}
struct VipMemberStruct: Codable {
let id: String
let _1: Int
}
struct RoleStruct: Codable {
let admin: AdminStruct?
let guest: GuestStruct?
let member: MemberStruct?
let vipMember: VipMemberStruct?
}
Given that enums are encoded into a nested structure, there are multiple CodingKeys declarations.
- One that contains the keys for each of the enum cases, which as before, is called
CodingKeys
. - One for each enum case that contains the keys for the associated values. These CodingKeys are prefixed with the capitalized case name, e.g.
AdminCodingKeys
for caseadmin
.
The compiler would generate something like this.
extension Role: Codable {
// contains keys for all cases of the enum
enum CodingKeys: CodingKey {
case admin
case guest
case member
case vipMember
}
// contains keys for all associated values of `case admin`
enum AdminCodingKeys: CodingKey {
}
// contains keys for all associated values of `case guest`
enum GuestCodingKeys: CodingKey {
case _0
}
// contains keys for all associated values of `case member`
enum MemberCodingKeys: CodingKey {
case id
}
// contains keys for all associated values of `case vipMember`
enum VipMemberCodingKeys: CodingKey {
case id
case _1
}
}
You can easily support sarunw.com by checking out this sponsor.
Debug 10x faster with Proxyman: Your ultimate tool to capture HTTPs requests/ responses, natively built for your iPhone and macOS. Special deal for Black Friday: Get 30% off for all Proxyman licenses with code “BLACKFRIDAY2024”.
Customization
We have learned that enum with associated values encoded into the nested structure and generated multiple CodingKeys under the hood.
You can think of it like a nested struct
. So you can customize it just like how you did with a struct.
Map case to other names
You can map any case to a different name by specifying a string value to CodingKeys
case. In this case, vipMember
will be mapped to the vip
.
extension Role: Codable {
// contains keys for all cases of the enum
enum CodingKeys: String, CodingKey {
case admin
case guest
case member
case vipMember = "vip"
}
...
}
Role.vipMember(id: "1234", 5)
will encode to the following JSON.
{
"vip" : {
"id" : "1234",
"_1" : 5
}
}
Map value to other names
You can map any value key by specifying a string value, but instead of doing it on CodingKeys
, you do this on an enum case coding keys.
In this case, numberOfYears
will be mapped to the second value of vipMember(id: String, Int)
. Notice that we need to do this on corresponding coding keys, VipMemberCodingKeys
.
extension Role: Codable {
...
enum VipMemberCodingKeys: String, CodingKey {
case id
case _1 = "numberOfYears"
}
}
Role.vipMember(id: "1234", 5)
will encode to the following JSON.
"vipMember" : {
"id" : "1234",
"numberOfYears" : 5
}
Exclude cases
You can control which cases in an enum should be codable by modifying CodingKeys
declarations. You can exclude any case by removing it from the CodingKeys
declaration.
In this case, we remove vipMember
from CodingKeys
. The compiler will no longer synthesize the code for vipMember
.
extension Role: Codable {
// contains keys for all cases of the enum
enum CodingKeys: CodingKey {
case admin
case guest
case member
// case vipMember
}
...
}
Exclude values
You can control which associated value in an enum case should be codable by modifying corresponding CodingKeys
.
You can safely exclude any value if you only conform to Encodable
. The excluded value will not be encoded to a JSON.
Role
only conforms to Encodable
here.
extension Role: Encodable {
...
enum VipMemberCodingKeys: CodingKey {
case id
// case _1
}
}
Role.vipMember(id: "1234", 5)
will encode to.
{
"vipMember" : {
"id" : "1234"
}
}
Things get a little complicated with Decodable
. Excluding any value mean the system won't know how to decode JSON to that particular value. This results in a compile error.
To fix the problem, values that are excluded must have a default value defined.
Add a default value to the excluded value (the second value) of vipMember
to satisfied Decodable
conformance and fix the error.
enum Role {
case admin
case guest(String?)
case member(id: String)
case vipMember(id: String, Int = 1)
}
extension Role: Codable {
enum VipMemberCodingKeys: CodingKey {
case id
// case _1
}
...
}
Read more article about Swift, Enum, Codable, or see all available topic
Enjoy the read?
If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.
Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.
If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.
Become a patron Buy me a coffee Tweet ShareHow to quickly test apps in other languages with an Xcode scheme
A tip for creating multiple schemes to quickly run your app in different languages.
How to test UI layout for different languages with Pseudolanguages
Each language has its own characteristic. Some are more verbose than others. Some have special characters that take up vertical spaces. Some even read and lay out from right to left. Let's see how to make sure your layout is ready for this.