Codable synthesis for enums with associated values in Swift 5.5

⋅ 4 min read ⋅ Swift Enum Codable

Table of Contents

In the first part of the two-part series on "Codable synthesis for enums with associated values", we learn how easy it is to make enums with associated values conform to Codable protocol and the behavior we got from the synthesis code.

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

Introduction

Enum is a very powerful language feature in Swift. I use it all the time in my code. When Apple introduces Codable API in Swift 4, they do a good job supporting automatic synthesizing Encodable and Decodable for enum with raw values.

We can make enum with raw values conform to the Codable protocol by just adopting it.

// 1
enum Role: String, Codable {
case guest
case member
}

struct User: Codable {
let name: String
// 2
let role: Role
}

1 We can make enum with raw value support Codable by just adopting it. By declaring protocol conformance, Role becomes a codable type.
2 We can use it in a codable context without doing any extra work.

With this minimal effort, we can decode JSON into our Swift model.

let jsonString = """
{
"name": "John",
"role": "guest"
}
"""

let user = try! JSONDecoder().decode(User.self, from: jsonString.data(using: .utf8)!)

Too bad Swift only support automatic Codable synthesis for enums with raw values, but for enum with associated values, you are on your own.

Swift 5.5 finally ends our suffering and extends the support for the automatic synthesis of these conformances to enums with associated values.

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

Sponsor sarunw.com and reach thousands of iOS developers.

Codable synthesis for enums with associated values

In Swift 5.5, you can make an enum with associated values conform to Codable by simply adopting it.

Adopting it is as easy as it could be, but what I want to know is how does this mapping from/to JSON look like and a way to customize it, which we are going to explore in this article.

We can make an enum with associated values conforming to Codable protocol without extra work.

enum Role: Codable {
case guest
case member(id: String)
}

Default structure of encoded enums

First, let's see how our JSON string looks when we encode our enum.

I will use the Role enum as an example. I have modified it to have four cases that should cover every scenario we usually encounter.

  1. No associated value.
  2. Optional unlabeled associated value.
  3. Labeled associated value.
  4. Mixed associated value.
enum Role: Codable {
case admin
case guest(String?)
case member(id: String)
case vipMember(id: String, Int) // id and number of years of a member.
}

Before we jump to the result, let's learn how the default implementation works.

  1. Enum becomes a container with a case as a key.
  2. Enum case key points to value container.
  3. Value container contains all associated values under that case.

Enum becomes a container with a case as a key

The default behavior will treat enum with associated value as a container with a single key that matched the name of the enum case.

{
"admin" : ...
}

{
"guest" : ...
}

{
"member" : ...
}

{
"vipMember" : ...
}

Enum case key points to another container

This single key case name points to another container.

{
"admin" : {
...
}
}

{
"guest" : {
...
}
}

{
"member" : {
...
}
}

{
"vipMember" : {
...
}
}

You can expect every case in an enum with associated value to have this structure. The only difference is in the inner container and case name.

Value container contains all associated values under that case

The inner container contain all associated values under a particular enum case. The tricky part of an enum that differs from structs and classes is that we always have a key for a value, a property name. But enum with associated values doesn't always have a key for its value.

Associated value can be in either of three forms.

  1. Labeled.
  2. Unlabeled.
  3. No associated value.

Let's use our Role as an example.

How associated values encoded

Labeled

This is the simplest form. The label will be used as a key when encoded.

Role.member(id: "1234") will encoded to.

{
"member" : {
"id" : "1234"
}
}

Unlabeled

Associated values can be unlabeled. In this case, a key will be generated in the form of _$N, where $N is the 0-based position of the parameter.

Role.guest("John") will be encoded to.

{
"guest" : {
"_0" : "John"
}
}

And

Role.vipMember(id: "1234", 5) will be encoded to.

{
"vipMember" : {
"id" : "1234",
"_1" : 5
}
}

No associated value

An enum case without associated values would be encoded as an empty container.

Role.admin will be encoded to.

{
"admin" : {}
}

An optional unlabeled value with a nil value is treated the same way as no value. Role.guest(nil) will be encoded to.

{
"guest" : {}
}

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

Sponsor sarunw.com and reach thousands of iOS developers.

Conclusion

In this article, we learn how easy it is to make enum with associated value conform to Codable. It is very simple but comes with naming that might not suit your need. The next part will explore how much we can customize it.


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 generate code coverage reports in Xcode

Xcode has a feature to generate code coverage since version 7. Let's see how to enable it and what you can expect from this feature.

Next
2021: Year in review

A look back at 2021.

← Home