Generating Codable Swift structs from JSON documents

Whenever you work with web APIs, the chances are high that you need to work with JSON. JSON is almost everywhere and there are some good reasons for that: It’s flexible, it’s easy to use and easy to read. And thanks to Swift’s Encodable and Decodable protocols, together with JSONEncoder and JSONDecoder, it’s also very easy to get into and out of your Swift projects.

However, in order to do so, you first need to write a Codable (or just Decodable or Encodable depending on your use case) type that more or less matches the structure of the JSON document before the above mentioned protocols and classes can do their magic. For example, take the following JSON document:

{
    “id”: “12”,
    “name”: “Barney”,
    “isAdmin”: true,
    “ownsProjects”: [23, 54]
}

A corresponding Swift type might looks like this:

struct Developer: Codable {
    let id: String
    let name: String
    let isAdmin: Bool
    let ownsProjects: [Int]
}

It’s usually pretty mechanical and easy to do but also pretty boring work.

🎊 json2codable 🎊

Over the last few days, I’ve had some free time and gave myself a small challenge to write a simple command line tool that takes a JSON document and prints out a Coding-conforming Swift struct that can be copied (or piped) into a Swift file, ready to be used.

The tool is called json2codable and can be found here. It runs on both macOS and Linux. 🎉

Challenges

I ran into a few challenges and one I want to write more about here is about finding the right Swift type for a property. JSON comes originally comes from the JavaScript world where some whacky things are allowed (from a Swift developers perspective. Sorry, no offence!), like putting different types into an array:

{
    “whackyArray”: [“name”, 123, bool, null, 3.1415]
}

It’s not super obvious what the Swift type for whackyArray should be. Although it is possible to build a type for such a construct, I ignored this extreme case for now. Any half-decent web service should not do this to you.

However, there are some similar, less extreme cases that I wanted to handle properly. Those cases are Optionality and Type Conversion.

Optionality

Having null values in JSON documents is very common and luckily, nullable values translate very nicely into Swifts Optional type. Optional is a generic type and it needs another type before it can become usable. The same is true when json2codable finds a null value. It can’t know what the actual type should be until it sees a non-null value in the same context. For example:

{
    “array”: [null, 123]
}

If the array only contained null, json2codable would abort because it can’t figure out what Swift type it should use. (Maybe a future version will add placeholder and just keep going). Only after it sees 123 it can conclude that the type must be Int?.

Type Conversion

This one is kind of similar to Optionality in that an inferred type might have to be converted to a different, more “capable” one in order to hold all the possible values. For example:

{
    “array”: [123, 3.1415]
}

While walking the array, json2codable first looks at 123 and infers a type of Int. Looking at the second element (3.1415) it needs to convert the Int type to a Double, which can hold both 123 and 3.1415.

Conclusion

json2codable is a command line tool that takes a JSON document and prints out a Swift Codable-conforming type. It’s not perfect and there are a bunch of enhancements I can think of for future versions. But the important part (to me at least) is that it was fun to build.

If you work a lot with JSON documents and need to parse them in your Swift project, please give it a go and feel free to leave some feedback.

Simon Stiefel @simonboots