How to Work With JSON in Swift

A basic to intermediate introduction to JSON and Swift

Josh Rondestvedt
The Startup

--

Photo by Safar Safarov on Unsplash

Introduction

JSON is one of the most common data formats for transferring data between servers and devices.

This will be a two part series. The first part will cover what JSON is and the basic handling in Swift while the second part will be focused on retrieving JSON from the web via URLSession and Alamofire.

If you would like to follow along, please download the sample project from my GitHub.

What is JSON?

JSON stands for JavaScript Object Notation. JSON is a lightweight format for storing and transporting data. The JSON format consists of keys and values. In Swift, think of this format as a dictionary where each key must be unique and the values can be strings, numbers, bools, or null (nothing). Values can also be another JSON object or an array of the types mentioned but let’s keep this simple to start with. JSON can be stored as a mulit-line String in Swift and converted to a Data object (and vice versa). This is done with the Decodable and Encodable protocols.

Below is a simple JSON object with two keys.

Creating a Model

The first step to convert a JSON object to a Swift type is to create a model. For our example above, here’s a struct we can use:

As you can see, each JSON key will be represented by the property in the model above. Be sure to conform to the Codable protocol so it can be used to decode and encode JSON.

Make sure you read the API’s documentation. There are instances where a key may or may not have value. If this is the case, you should mark that property as optional.

Nested JSON Objects

You most certainly will run into scenarios with nested JSON objects. Here’s a straightforward approach to handling this:

Decodable and Encodable Protocols

Swift has three protocols that makes working with JSON simple and straightforward — Decodable, Encodable, and Codable. Codable is a type alias for Decodable and Encodable which is done through Swift’s protocol composition. Let’s go through each of these.

Encodable

By marking your model type as Encodable, you tell Swift that you want to use this type to convert instances of Person to a JSON object. Most commonly this is used to send data to the server.

Decodable

By marking your model type as Decodable, you tell Swift that you want to use this type to convert JSON to Person instances. Most commonly this is used when receiving data from the server.

Codable

As mentioned earlier in this article, Codable is a type alias for both Encodable and Decodable which allows your type to be used to encode and decode JSON. Codable was introduced in Swift 4. Whether you mark your type as Encodable, Decode, or Codable, is up to you. Some may prefer to be explicit with their naming and stick with the Encodable and Decodable, but just mark as Codable unless told otherwise.

Coding Keys

The property naming convention in Swift is camelCase. Here are a few examples of common naming conventions in programming:

  • camelCase
  • snake_case
  • Pascal_Case

Some APIs will have keys formatted other than camelCase. To keep your code clean and follow the Swift convention, Swift offers a CodingKey protocol. This protocol will tell your program to use custom keys while keeping the camelCase convention. The convention is to create an enum named CodingKeys inside the type.

Custom decoder

Sometimes you may want to flatten the JSON into a single type. This can be done by creating a custom decoder initializer. I created a new type and JSON string to help illustrate this process more clearly.

This approach isn’t limited to one level. You can go several levels deep, but it can get complex quickly and readability might suffer.

JSONDecoder and JSONEncoder

Swift has two classes for handling JSON — JSONDecoder and JSONEncoder. Well, technically, Swift has a third, JSONSerialization, but we will just use the two I just mentioned.

JSONDecoder

Decoding allows Swift to convert a JSON object to a Swift type. Using our example from above, let’s create an instance of JSONDecoder. Below are the steps to convert a JSON string to our Person type:

  1. Our model that we will use to convert the JSON string to a Swift type.
  2. Convert the JSON string to a Data type
  3. Create an instance of JSONDecoder
  4. Instead of using the CodingKey protocol, there is a property on the JSONDecoder class called keyDecodingStrategy that makes converting a JSON key formatted as snake_case to Swift's preferred camelCase. Nice and easy!
  5. JSONDecoder has a decode method that converts our data object into our desired Swift type. This method is a throwing method so it should be handled inside a do catch block.

JSONEncoder

Encoding allows Swift to convert a Swift type into a valid JSON object.

Setting the outputFormatting property to .prettyPrinted allows the JSON to, well, be pretty when printed to the console.

Date Formats

iso8601 — A date formatted as: 1990–12–15T00:00:00Z. This will be a String type in the JSON object. To convert this value to a Swift Date object, set the dateDecodingStrategy to .iso8601.

secondsSince1970 — A date formatted as: 661219200. This will be a Int type in the JSON object. To convert this value to a Swift Date object, set the dateDecodingStrategy to .secondsSince1970.

Custom — A date can be formatted as a string in several different ways. This is where the custom option is required. Create an instance of the DateFormatter class and set the dateFormat with the custom string representing the data format.

Summary

As you can see, working with JSON is pretty painless in Swift. Nested JSON can get complex quickly but at least you have more than one option for this scenario. As I mentioned above, this will be a two part series. In the next article, I am going to demonstrate how to retrieve JSON from the web using both URLSession and the third party networking library Alamofire.

Resources

--

--

Josh Rondestvedt
The Startup

Software Developer at Branch. Founder of the iOS app seventytwo. I build cool software for Apple devices.