How to Read and Write JSON in Swift
Here we will explain how to read and write JSON in Swift.
Convert a JSON String to a Swift Object
Here we'll use JSONDecoder to convert a JSON string into a Swift struct instance.
We'll convert the following JSON string:
{
"name": "Emma",
"age": 20,
"email": "emma@mail.com"
}
Define a struct to hold the JSON data.
To decode using JSONDecoder, the type must conform to the Decodable protocol.
Codable is a type alias for both Decodable and Encodable, so conforming to Codable also works.
Define a Person struct as follows. If the property names don't match the JSON keys, decoding will fail.
struct Person: Decodable {
var name: String
var age: Int
var email: String
}
To convert the JSON string above into Person using JSONDecoder, do the following:
import Foundation
struct Person: Decodable {
var name: String
var age: Int
var email: String
}
let jsonData = """
{
"name": "Emma",
"age": 20,
"email": "emma@mail.com"
}
""".data(using: .utf8)!
let person = try JSONDecoder().decode(Person.self, from: jsonData)
print(person.name)
print(person.age)
print(person.email)
The output will be as follows, showing that the JSON was converted into a person object:
Emma
20
emma@mail.com
Convert a JSON String to a Swift Object with Extra Properties
Earlier we defined a struct whose properties matched the JSON exactly, but you may want to add additional properties to the struct.
If you simply add properties that don't exist in the JSON to the struct above, decoding will fail with an error.
For example, if you add a nickname property to the Person struct and run the code, you'll see an error like this:
Playground execution terminated: An error was thrown and was not caught.
If we dump the error using do-catch, we get the following error.
import Foundation
struct Person: Decodable {
var name: String
var age: Int
var email: String
var nickname: String
}
let jsonData = """
{
"name": "Emma",
"age": 20,
"email": "emma@mail.com"
}
""".data(using: .utf8)!
do {
let person = try JSONDecoder().decode(Person.self, from: jsonData)
print(person.name)
print(person.age)
print(person.email)
} catch {
dump(error)
}
▿ Swift.DecodingError.keyNotFound
▿ keyNotFound: (2 elements)
- .0: CodingKeys(stringValue: "nickname", intValue: nil)
▿ .1: Swift.DecodingError.Context
- codingPath: 0 elements
- debugDescription: "No value associated with key CodingKeys(stringValue: \"nickname\", intValue: nil) (\"nickname\")."
- underlyingError: nil
When you want the Swift type to have properties that aren't present in the JSON, you can avoid errors by giving those additional properties default values and defining CodingKeys to list only the properties that are decoded from the JSON.
import Foundation
struct Person: Decodable {
var name: String
var age: Int
var email: String
var nickname: String = ""
private enum CodingKeys: String, CodingKey {
case name, age, email
}
}
let jsonData = """
{
"name": "Emma",
"age": 20,
"email": "emma@mail.com"
}
""".data(using: .utf8)!
do {
let person = try JSONDecoder().decode(Person.self, from: jsonData)
print(person.name)
print(person.age)
print(person.email)
} catch {
dump(error)
}
The result is as follows; decoding succeeds without an error:
Emma
20
emma@mail.com
Convert a Swift Object to a JSON String
Now let's do the opposite: convert a Swift object into a JSON string.
We'll use JSONEncoder to generate a JSON string from a Swift struct.
To encode using JSONEncoder, the type must conform to the Encodable protocol.
Codable is a type alias for Decodable and Encodable, so conforming to Codable also works.
To generate a JSON string from a Person struct instance, do the following:
import Foundation
struct Person: Encodable {
var name: String
var age: Int
var email: String
var nickname: String
}
let person = Person(name: "Michael", age: 16, email: "michael@gmail.com", nickname: "Mike")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(person)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
The output will look like this, confirming that person was converted to a JSON string:
{
"age" : 16,
"name" : "Michael",
"nickname" : "Mike",
"email" : "michael@gmail.com"
}
Convert a Swift Object to a JSON String with Selected Properties
If you want to convert only specific properties of a Swift object into a JSON string, define CodingKeys and list only the properties you want to include in the JSON.
For example, if you want to output only name and email from the Person struct, you can do the following:
import Foundation
struct Person: Encodable {
var name: String
var age: Int
var email: String
var nickname: String
private enum CodingKeys: String, CodingKey {
case name, email
}
}
let person = Person(name: "Michael", age: 16, email: "michael@gmail.com", nickname: "Mike")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(person)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
The output will be as follows, with only "name" and "email" included in the JSON string:
{
"name" : "Michael",
"email" : "michael@gmail.com"
}
If the type conforms to Codable (not just Encodable), then to satisfy Decodable you need to provide default values for any properties that are not included in CodingKeys.
That wraps up our explanation of how to read and write JSON in Swift.