How to Work With JSON in Swift
A basic to intermediate introduction to JSON and Swift
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:
- Our model that we will use to convert the JSON string to a Swift type.
- Convert the JSON string to a Data type
- Create an instance of
JSONDecoder
- Instead of using the CodingKey protocol, there is a property on the
JSONDecoder
class calledkeyDecodingStrategy
that makes converting a JSON key formatted as snake_case to Swift's preferred camelCase. Nice and easy! 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.