Deserializing JSON into your Swift objects is a common task when you’re getting data from an API.
Swift has made this a lot easier over the years with Decodable
. Decodable allows you to deserialize json into Swift objects in just a couple of lines.
We’re going to use JSONPlaceholder as our test API. This site provides APIs that return data for testing and learning purposes.
Check out the return of this url - https://jsonplaceholder.typicode.com/todos/1
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
This is the data we will be using for our example.
Next let’s create the Swift model that we’ll be using to deserialize the json into.
struct Todo: Codable {
let userId : Int
let id : Int
let title : String
let completed : Bool
}
Notice that we’ve named our properties exactly the same as they were in the JSON.
Now that we’ve created our struct, let’s decode the JSON into it.
We’ll first create the request like so:
guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
// Check data is not nil
guard let data = data else { return }
// TODO: Deserialize the data into Swift object
}.resume()
Now let’s add the deserializing code. Since we used Decodable
this part is easy.
let todo = try JSONDecoder().decode(Todo.self, from: data)
All together we have this:
guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
// Check data is not nil
guard let data = data else { return }
do {
let todo = try JSONDecoder().decode(Todo.self, from: data)
print(todo.userId)
print(todo.id)
print(todo.title)
print(todo.completed)
} catch let error {
print("error!")
}
}.resume()
Your console should print the properties like so:
1
1
delectus aut autem
false
That’s it! Deserializing JSON into Swift objects is made simple with Decodable
.
If you’d like to run this on your computer, download the Swift Playground here.
What if we want to name our model properties something different to what they’re defined as in the JSON?
To do this we need to use a CodingKey
enum.
Let’s rename our struct’s properties first.
struct Todo: Codable {
let userNumber : Int
let todoId : Int
let todoTitle : String
let isCompleted : Bool
}
None of these properties names match their corresponding JSON names.
How can we get this to deserialize correctly?
By defining a CodingKey
the enum in your struct:
struct Todo: Codable {
let userNumber : Int
let todoId : Int
let todoTitle : String
let isCompleted : Bool
enum CodingKeys: String, CodingKey {
case userNumber = "userId"
case todoId = "id"
case todoTitle = "title"
case isCompleted = "completed"
}
}
Now if we run our code again (modifying the print statements with the new property names), we’ll get the same result:
1
1
delectus aut autem
false
You can download the Swift Playground demonstrating this here.
What if the way your data in Swift and JSON are structured differently?
You can deserialize and restructure the data at the same time by using a CodingKey
as well!
Let’s take a look at this JSON example from (http://jsonplaceholder.typicode.com/users/1)
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "[email protected]",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
You can see we have nested properties here. What if we want to just get the user’s name, phone-number and company name?
We would create a Swift struct that extends Decodable
like so:
struct Client: Decodable {
let name : String
let phone : String
let companyName : String
}
Notice our Swift struct and our JSON do not have the same data structure.
Our Swift object will be flat with only three properties while the JSON has many properties, some of which are nested.
Now to transform the data and deserialize it at the same time, we will create a couple of Coding Keys.
enum CodingKeys: String, CodingKey {
case name
case phone
case company
}
enum CompanyKeys: String, CodingKey {
case companyName = "name"
}
Lastly we’ll need to create a custom init from decoder function:
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
phone = try values.decode(String.self, forKey: .phone)
let company = try values.nestedContainer(keyedBy: CompanyKeys.self, forKey: .company)
companyName = try company.decode(String.self, forKey: .companyName)
}
Let’s test this out!
func getClient() {
// Check URL is valid
guard let url = URL(string: "http://jsonplaceholder.typicode.com/users/1") else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
// Check data is not nil
guard let data = data else { return }
do {
let client = try JSONDecoder().decode(Client.self, from: data)
print(client)
} catch {
print("error!")
}
}.resume()
}
getClient()
This code will result in the console output of:
Client(name: "Leanne Graham", phone: "1-770-736-8031 x56442", companyName: "Romaguera-Crona")
That’s how to deserialize and restructure your JSON data in Swift.
You can get the full source code here.
So far we’ve only talked about deserializing JSON into Swift objects.
However, if you need to send JSON data to a backend, you’ll need to serialize your Swift objects first.
This is quite similar to deserialization.
Make sure your struct conforms to Encodable
or Codable
:
struct Todo: Codable {
let userId : Int
let id : Int
let title : String
let completed : Bool
}
Let’s create a Todo
object:
let myTodo = Todo(userId: 1, id: 1, title: "Finish json swift tutorial", completed: false)
Finally let’s encode this into JSON using JSONEncoder
:
let encoder = JSONEncoder()
// Output formatting is optional, just so it looks good in console
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(myTodo)
print(String(data: data, encoding: .utf8)!)
This should result in the console output:
{
"id" : 1,
"title" : "Finish json swift tutorial",
"userId" : 1,
"completed" : false
}
Download the encoding example source code here.
You may be wondering what’s the difference between Codable vs Decodable vs Encodable?
Decodable allows an object to be decoded using the JSONDecoder.
Encodable allows an object to be encoded using the JSONEncoder.
Codable allows an object to be encoded and decoded using JSONEncoder and JSONDecoder respectively.
The Complete iOS App Development Bootcamp
Disclosure: This website may contain affiliate links, meaning when you click the links and make a purchase, we receive a commission.