How to customize automatic synthesizing Codable for enums with associated values

⋅ 5 min read ⋅ Swift Enum Codable

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.

  1. Codable synthesis for enums with associated values
  2. Customization

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

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 case admin.

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.

Sponsor sarunw.com and reach thousands of iOS developers.

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.

Type 'Role' does not conform to protocol 'Decodable' error.
Type 'Role' does not conform to protocol 'Decodable' 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 Share
Previous
How 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.

Next
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.

← Home