How to decode snake case with Codable
Forget CodingKeys. There is much better solution available!
Published: March 17, 2021 Sponsored App StoreIn this shorter post, let's go over decoding API response whose keys follow the snake case convention 🐍. Snake case means that property siteName
in Swift (which uses the camelCase 🐪) will be site_name
in snakecase. This is quite common with APIs because a fair bit of backend languages use snakecase.
I guess in the past you probably used the CodingKey
approach which lets you manually remap property names.
Sample JSON
As an example I am going to use data from my iOS Feeds API. Here is a sample:
[
{
"title": "Announcing: isowords",
"site_name": "Point-Free Pointers",
"created": "2021-03-17T08:04:50.272092Z",
"guid": "https://www.pointfree.co/blog/posts/54-announcing-isowords",
"url": "https://iosfeeds.com/read/10047",
"twitter_url": "https://twitter.com/pointfreeco"
},
{
"title": "Reading environment variables from iOS and macOS unit tests",
"site_name": "Igor Kulman’s Blog",
"created": "2021-03-17T08:02:57.829545Z",
"guid": "https://blog.kulman.sk/reading-environment-variables-from-unit-tests/",
"url": "https://iosfeeds.com/read/10045",
"twitter_url": "https://twitter.com/igorkulman"
},
{
"title": "SwiftUI by Tutorials [SUBSCRIBER]",
"site_name": "Ray Wenderlich",
"created": "2021-03-17T00:05:22.937969Z",
"guid": "https://www.raywenderlich.com/books/swiftui-by-tutorials/v3.0",
"url": "https://iosfeeds.com/read/10044",
"twitter_url": "https://twitter.com/rwenderlich"
},
]
We basically have an array of articles. And we can model simple version in Swift like this:
struct Article: Decodable {
let title: String
let siteName: String
let twitterUrl: String
}
However by default, this is not going to work, because JSONDecoder
won't be able to automatically parse site_name
and twitter_url
into siteName
and twitterUrl
respectively.
So at this point we would create the CodingKeys
enum which conforms to CodingKey
protocol and re-mapped the names this way. But that is not necessary.
Decoding snake case
JSONDecoder
has property called keyDecodingStrategy and you can set it to .convertFromSnakeCase
like this:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
And done! Snake case will be mapped correctly to camel case. If you want to try this yourself, below is shortest Playground example I could come up with:
import UIKit
struct Article: Decodable {
let title: String
let siteName: String
let twitterUrl: String
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
guard let data = try? Data(contentsOf: URL(string: "https://iosfeeds.com/api/articles/")!) else {
fatalError()
}
if let decoded = try? decoder.decode([Article].self, from: data) {
print(decoded)
}
I think this approach is pretty nice, assuming you don't want to also rename certain properties to provide additional clarity in your Swift code.
CodingKeys example
Just for contrast, here is the Article
struct with CodingKey
implemented:
struct Article: Decodable {
let title: String
let siteName: String
let twitterUrl: String
enum CodingKeys: String, CodingKey {
case title
case siteName = "site_name"
case twitterUrl = "twitter_url"
}
}
This is not too bad, but your API response might have 15 keys or more..
It might also make sense to refactor this decoder settings into a subclass:
class SnakeCaseJSONDecoder: JSONDecoder {
override init() {
super.init()
keyDecodingStrategy = .convertFromSnakeCase
}
}
And then use it like this:
let decoder = SnakeCaseJSONDecoder()
Uses: Xcode 12 & Swift 5.3