How To Deserialize Json To Swift

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.

JSON Deserializing To Swift Example

We’re going to use JSONPlaceholder as our test API. This site provides APIs that return data for testing and learning purposes.

JSON Test Data

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.

Swift Model File

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.

How To Decode Into Swift Object

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.

Model Properties Names Don’t Match JSON

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.

JSON Structure And Swift Structure Are Different

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.

JSON Serializing Or Encoding In Swift

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.

Codable vs Decodable vs Encodable

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.

If you liked this post and want to learn more, check out The Complete iOS Developer Bootcamp. Speed up your learning curve - hundreds of students have already joined. Thanks for reading!

Eddy Chung

I teach iOS development on ZeroToAppStore.com.

Similar Posts