How to decode snake case with Codable

Forget CodingKeys. There is much better solution available!

Published: March 17, 2021
App Store

In 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

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀