iOS Programming · · 18 min read

Working with JSON and Codable in Swift 5

Working with JSON and Codable in Swift 5

First, what’s JSON? JSON (short for JavaScript Object Notation) is a text-based, lightweight, and easy way for storing and exchanging data. It’s commonly used for representing structural data and data interchange in client-server applications, serving as an alternative to XML. A lot of the web services we use every day have JSON-based APIs. Most of the iOS apps, including Twitter, Facebook, and Flickr send data to their backend web services in JSON format.

Editor’s note: This is a sample chapter of the Intermediate iOS 13 Programming with Swift book.

As an example, here is a JSON representation of a sample Movie object:

{
    "title": "The Amazing Spider-man",
    "release_date": "03/07/2012",
    "director": "Marc Webb",
    "cast": [
        {
            "name": "Andrew Garfield",
            "character": "Peter Parker"
        },
        {
            "name": "Emma Stone",
            "character": "Gwen Stacy"
        },
        {
            "name": "Rhys Ifans",
            "character": "Dr. Curt Connors"
        }
    ]
}

As you can see, JSON formatted data is more human-readable and easier to parse than XML. I’ll not go into the details of JSON. This is not the purpose of this chapter. If you want to learn more about the technology, I recommend you to check out the JSON Guide at http://www.json.org/.

Since the release of iOS 5, the iOS SDK has already made it easy for developers to fetch and parse JSON data. It comes with a handy class called NSJSONSerialization, which can automatically convert JSON formatted data to objects. Later in this chapter, I will show you how to use the API to parse some sample JSON formatted data, returned by a web service. Once you understand how it works, it is fairly easy to build an app by integrating with other free/paid web services.

Since the release of Swift 4, Apple introduced the Codable protocol to simplify the whole JSON archival and serialization process. We will also look into this new feature and see how we can apply it in JSON parsing.

JSON Demo App

As usual, we’ll create a demo app. Let’s call it KivaLoan. The reason why we name the app KivaLoan is that we will utilize a JSON-based API provided by Kiva.org. If you haven’t heard of Kiva, it is a non-profit organization with a mission to connect people through lending to alleviate poverty. It lets individuals lend as little as $25 to help create opportunities around the world. Kiva provides free web-based APIs for developers to access their data. For our demo app, we’ll call up the following Kiva API to retrieve the most recent fundraising loans and display them in a table view:

https://api.kivaws.org/v1/loans/newest.json

Quick note: Starting from iOS 9, Apple introduced a feature called App Transport Security (ATS) with the aim to improve the security of connections between an app and web services. By default, all outgoing connections should ride on HTTPS. Otherwise, your app will not be allowed to connect to the web service. Optionally, you can add a key named NSAllowsArbitraryLoads in the Info.plist and set the value to YES to disable ATS, so that you can connect to web APIs over HTTP.

However, you’ll have to take note if you use NSAllowsArbitraryLoads in your apps. In iOS 10, Apple further enforces ATS for all iOS apps. By January 2017, all iOS apps should be ATS-compliant. In other words, if your app connects to external any web services, the connection must be over HTTPS. If your app can’t fulfil this requirement, Apple will not allow it to be released on the App Store.

The returned data of the above API is in JSON format. Here is a sample result:

loans: (
        {
        activity = Retail;
        "basket_amount" = 0;
        "bonus_credit_eligibility" = 0;
        "borrower_count" = 1;
        description =         {
            languages =             (
                fr,
                en
            );
        };
        "funded_amount" = 0;
        id = 734117;
        image =         {
            id = 1641389;
            "template_id" = 1;
        };
        "lender_count" = 0;
        "loan_amount" = 750;
        location =         {
            country = Senegal;
            "country_code" = SN;
            geo =             {
                level = country;
                pairs = "14 -14";
                type = point;
            };
        };
        name = "Mar\U00e8me";
        "partner_id" = 108;
        "planned_expiration_date" = "2016-08-05T09:20:02Z";
        "posted_date" = "2016-07-06T09:20:02Z";
        sector = Retail;
        status = fundraising;
        use = "to buy fabric to resell";
    },
....
....
)

You will learn how to use the NSJSONSerialization class to convert the JSON formatted data into objects. It’s unbelievably simple. You’ll see what I mean in a while.

To keep you focused on learning the JSON implementation, you can first download the project template from http://www.appcoda.com/resources/swift5/KivaLoanStarter.zip. I have already created the skeleton of the app for you. It is a simple table-based app that displays a list of loans provided by Kiva.org. The project template includes a pre-built storyboard and custom classes for the table view controller and prototype cell. If you run the template, it should result in an empty table app.

swift-json-codable-project

Creating JSON Data Model

We will first create a class to model a loan. It’s not required for loading JSON but the best practice is to create a separate class (or structure) for storing the data model. The Loan class represents the loan information in the KivaLoan app and is used to store the loan information returned by Kiva.org. To keep things simple, we won’t use all the returned data of a loan. Instead, the app will just display the following fields of a loan:

  • Name of the loan applicant
name = "Mar\U00e8me";
  • Country of the loan applicant
location =         {
            country = Senegal;
            "country_code" = SN;
            geo =             {
                level = country;
                pairs = "14 -14";
                type = point;
            };
        };
  • How the loan will be used
use = "to buy fabric to resell";
  • Amount
"loan_amount" = 750;

These fields are good enough for filling up the labels in the table view. Now create a new class file using the Swift File template. Name it Loan.swift and declare the Loan structure like this:

struct Loan {

    var name: String = ""
    var country: String = ""
    var use: String = ""
    var amount: Int = 0

}

JSON supports a few basic data types including number, String, Boolean, Array, and Objects (an associated array with key and value pairs).

For the loan fields, the loan amount is stored as a numeric value in the JSON-formatted data. This is why we declared the amount property with the type Int. For the rest of the fields, they are declared with the type String.

Fetching Loans with the Kiva API

As I mentioned earlier, the Kiva API is free to use. No registration is required. You may point your browser to the following URL and you’ll get the latest fundraising loans in JSON format.

https://api.kivaws.org/v1/loans/newest.json

Okay, let’s see how we can call up the Kiva API and parse the returned data. First, open KivaLoanTableViewController.swift and declare two variables at the very beginning:

private let kivaLoanURL = "https://api.kivaws.org/v1/loans/newest.json"
private var loans = [Loan]()

We just defined the URL of the Kiva API, and declare the loans variable for storing an array of Loan objects. Next, insert the following methods in the same file:

func getLatestLoans() {
    guard let loanUrl = URL(string: kivaLoanURL) else {
        return
    }

    let request = URLRequest(url: loanUrl)
    let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in

        if let error = error {
            print(error)
            return
        }

        // Parse JSON data
        if let data = data {
            self.loans = self.parseJsonData(data: data)

            // Reload table view
            OperationQueue.main.addOperation({ 
                self.tableView.reloadData()
            })
        }
    })

    task.resume()
}

func parseJsonData(data: Data) -> [Loan] {

    var loans = [Loan]()

    do {
        let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary

        // Parse JSON data
        let jsonLoans = jsonResult?["loans"] as! [AnyObject]
        for jsonLoan in jsonLoans {
            let loan = Loan()
            loan.name = jsonLoan["name"] as! String
            loan.amount = jsonLoan["loan_amount"] as! Int
            loan.use = jsonLoan["use"] as! String
            let location = jsonLoan["location"] as! [String:AnyObject]
            loan.country = location["country"] as! String
            loans.append(loan)
        }

    } catch {
        print(error)
    }

    return loans
}

These two methods form the core part of the app. Both methods work collaboratively to call the Kiva API, retrieve the latest loans in JSON format and translate the JSON-formatted data into an array of Loan objects. Let’s go through them in detail.

In the getLatestLoans method, we first instantiate the URL structure with the URL of the Kiva Loan API. The initialization returns us an optional. This is why we use the guard keyword to see if the optional has a value. If not, we simply return and skip all the code in the method.

Next, we create a URLSession with the load URL. The URLSession class provides APIs for dealing with online content over HTTP and HTTPS. The shared session is good enough for making simple HTTP/HTTPS requests. In case you have to support your own networking protocol, URLSession also provides you an option to create a custom session.

One great thing of URLSession is that you can add a series of session tasks to handle the loading of data, as well as uploading and downloading files and data fetching from servers (e.g. JSON data fetching).

With sessions, you can schedule three types of tasks: data tasks (URLSessionDataTask) for retrieving data to memory, download tasks (URLSessionDownloadTask) for downloading a file to disk, and upload tasks (URLSessionUploadTask) for uploading a file from disk. Here we use the data task to retrieve contents from Kiva.org. To add a data task to the session, we call the dataTask method with the specific URL request. After you add the task, the session will not take any action. You have to call the resume method (i.e. task.resume()) to initiate the data task.

Like most networking APIs, the URLSession API is asynchronous. Once the request completes, it returns the data (as well as errors) by calling the completion handler.

In the completion handler, immediately after the data is returned, we check for an error. If no error is found, we invoke the parseJsonData method.

The data returned is in JSON format. We create a helper method called parseJsonData for converting the given JSON-formatted data into an array of Loan objects. The Foundation framework provides the JSONSerialization class, which is capable of converting JSON to Foundation objects and converting Foundation objects to JSON. In the code snippet, we call the jsonObject method with the given JSON data to perform the conversion.

When converting JSON formatted data to objects, the top-level item is usually converted to a Dictionary or an Array. In this case, the top level of the returned data of the Kiva API is converted to a dictionary. You can access the array of loans using the key loans.

How do you know what key to use?

You can either refer to the API documentation or test the JSON data using a JSON browser (e.g. http://jsonviewer.stack.hu). If you’ve loaded the Kiva API into the JSON browser, here is an excerpt from the result:

{
  "paging": {
    "page": 1,
    "total": 5297,
    "page_size": 20,
    "pages": 265
  },
  "loans": [
    {
      "id": 794429,
      "name": "Joel",
      "description": {
        "languages": [
          "es",
          "en"
        ]
      },
      "status": "fundraising",
      "funded_amount": 0,
      "basket_amount": 0,
      "image": {
        "id": 1729143,
        "template_id": 1
      },
      "activity": "Home Appliances",
      "sector": "Personal Use",
      "use": "To buy home appliances.",
      "location": {
        "country_code": "PE",
        "country": "Peru",
        "town": "Ica",
        "geo": {
          "level": "country",
          "pairs": "-10 -76",
          "type": "point"
        }
      },
      "partner_id": 139,
      "posted_date": "2015-11-20T08:50:02Z",
      "planned_expiration_date": "2016-01-04T08:50:02Z",
      "loan_amount": 400,
      "borrower_count": 1,
      "lender_count": 0,
      "bonus_credit_eligibility": true,
      "tags": [

      ]
    },
    {
      "id": 797222,
      "name": "Lucy",
      "description": {
        "languages": [
          "en"
        ]
      },
      "status": "fundraising",
      "funded_amount": 0,
      "basket_amount": 0,
      "image": {
        "id": 1732818,
        "template_id": 1
      },
      "activity": "Farm Supplies",
      "sector": "Agriculture",
      "use": "To purchase a biogas system for clean cooking",
      "location": {
        "country_code": "KE",
        "country": "Kenya",
        "town": "Gatitu",
        "geo": {
          "level": "country",
          "pairs": "1 38",
          "type": "point"
        }
      },
      "partner_id": 436,
      "posted_date": "2016-11-20T08:50:02Z",
      "planned_expiration_date": "2016-01-04T08:50:02Z",
      "loan_amount": 800,
      "borrower_count": 1,
      "lender_count": 0,
      "bonus_credit_eligibility": false,
      "tags": [

      ]
    },

     ...

As you can see from the above code, paging and loans are two of the top-level items. Once the JSONSerialization class converts the JSON data, the result (i.e. jsonResult) is returned as a Dictionary with the top-level items as keys. This is why we can use the key loans to access the array of loans. Here is the line of code for your reference:

let jsonLoans = jsonResult?["loans"] as! [AnyObject]

With the array of loans (i.e. jsonLoans) returned, we loop through the array. Each of the array items (i.e. jsonLoan) is converted into a dictionary. In the loop, we extract the loan data from each of the dictionaries and save them in a Loan object. Again, you can find the keys (highlighted in yellow) by studying the JSON result. The value of a particular result is stored as AnyObject. AnyObject is used because a JSON value could be a String, Double, Boolean, Array, Dictionary or null. This is why you have to downcast the value to a specific type such as String and Int. Lastly, we put the loan object into the loans array, which is the return value of the method.

for jsonLoan in jsonLoans {
    var loan = Loan()

    loan.name = jsonLoan["name"] as! String
    loan.amount = jsonLoan["loan_amount"] as! Int
    loan.use = jsonLoan["use"] as! String
    let location = jsonLoan["location"] as! [String: AnyObject]
    loan.country = location["country"] as! String

    loans.append(loan)
}

After the JSON data is parsed and the array of loans is returned, we call the reloadData method to reload the table. You may wonder why we need to call OperationQueue.main.addOperation and execute the data reload in the main thread.

The block of code in the completion handler of the data task is executed in a background thread. If you call the reloadData method in the background thread, the data reload will not happen immediately. To ensure a responsive GUI update, this operation should be performed in the main thread. This is why we call the OperationQueue.main.addOperation method and request to run the reloadData method in the main queue.

OperationQueue.main.addOperation({ 
    self.tableView.reloadData()
})

Quick note: You can also use dispatch_async function to execute a block of code in the main thread. But according to Apple, it is recommended to use OperationQueue over dispatch_async. As a general rule, Apple recommends using the highest-level APIs rather than dropping down to the low-level ones.

Displaying Loans in A Table View

With the loans array in place, the last thing we need to do is to display the data in the table view. Update the following methods in KivaLoanTableViewController.swift:

override func numberOfSections(in tableView: UITableView) -> Int {
    // Return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // Return the number of rows
    return loans.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! KivaLoanTableViewCell

    // Configure the cell...
    cell.nameLabel.text = loans[indexPath.row].name
    cell.countryLabel.text = loans[indexPath.row].country
    cell.useLabel.text = loans[indexPath.row].use
    cell.amountLabel.text = "$\(loans[indexPath.row].amount)"

    return cell
}

The above code is pretty straightforward if you are familiar with the implementation of UITableView. In the tableView(_:cellForRowAt:) method, we retrieve the loan information from the loans array and populate them in the custom table cell. One thing to take note of is the code below:

"$\(loans[indexPath.row].amount)"

In some cases, you may want to create a string by adding both string (e.g. $) and integer (e.g. loans[indexPath.row].amount) together. Swift provides a powerful way to create these kinds of strings, known as string interpolation. You can make use of it by using the above syntax.

Lastly, insert the following line of code in the viewDidLoad method to start fetching the loan data:

getLatestLoans()

Compile and Run the App

Now it’s time to test the app. Compile and run it in the simulator. Once launched, the app will pull the latest loans from Kiva.org and display them in the table view.

swift-json-codable-kiva-app

For reference, you can download the complete Xcode project from http://www.appcoda.com/resources/swift5/KivaLoan.zip.

Introducing Codable

Since the release of Swift 4, Apple introduced a new way to encode and decode JSON data using Codable. We will rewrite the JSON decoding part of the demo app using this new approach.

Before we jump right into the modification, let me give you a basic walkthrough of Codable. If you look into the documentation of Codable, it is just a type alias of a protocol composition:

typealias Codable = Decodable & Encodable

Decodable and Encodable are the two actual protocols you need to work with. However, for convenience’s sake, we usually refer to this type alias for handling JSON encoding and decoding.

First, what’s the advantage of using Codable over the traditional approach for encoding/decoding JSON? If you go back to the previous section and read the code again, you will notice that we had to manually parse the JSON data, convert it into dictionaries and create the Loan objects.

Codable simplifies the whole process by offering developers a different way to decode (or encode) JSON. As long as your type conforms to the Codable protocol, together with the new JSONDecoder, you will be able to decode the JSON data into your specified instances. The below figure illustrates the decoding of a sample loan data into an instance of Loan using JSONDecoder.

Decoding JSON

To give you a better idea about how Codable works, let’s start a Playground project and write some code. Once you have created your Playground project, declare the following json variable:

let json = """
{

"name": "John Davis",
"country": "Peru",
"use": "to buy a new collection of clothes to stock her shop before the holidays.",
"amount": 150

}
"""

We will first start with the basics. Here we define a very simple JSON data with 4 items. The value of the first three items are of the type String and the last one is of the type Int. As a side note, if this is the first time you see the pair of triple quotes ("""), this syntax was introduced in Swift 4 for declaring strings with multi-lines.

Next, declare the Loan structure like this:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int
}

This Loan structure is very similar to the one we defined in the previous section, except that it adopts the Codable protocol. You should also note that the property names match those of the JSON data.

Now, let’s see the magic!

Continue to insert the following code in your Playground file:

let decoder = JSONDecoder()

if let jsonData = json.data(using: .utf8) {

    do {
        let loan = try decoder.decode(Loan.self, from: jsonData)
        print(loan)

    } catch {
        print(error)
    }
}

In the code above, we instantiate an instance of JSONDecoder and then convert the JSON string we defined earlier into Data. The magic happens in this line of code:

let loan = try decoder.decode(Loan.self, from: jsonData)

You just need to call the decode method of the decoder with the JSON data and specify the type of the value to decode (i.e. Loan.self). The decoder will automatically parse the JSON data and convert them into a Loan object.

If you’ve done it correctly, you should see this line in the console:

Loan(name: "John Davis", country: "Peru", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)

Cool, right?

JSONDecoder automatically decodes the JSON data and stores the decoded value in the corresponding property of the specified type (here, it is Loan).

Working with Custom Property Names

Earlier, I showed you the simplest example of JSON decoding. However, the decoding process is not always so straightforward. Now, let’s take a look another example.

Sometimes, the property name of your type and the key of the JSON data are not exactly matched. How can you perform the decoding?

Let’s say, we define the json variable like this:

let json = """
{

"name": "John Davis",
"country": "Peru",
"use": "to buy a new collection of clothes to stock her shop before the holidays.",
"loan_amount": 150

}
"""

In the JSON data, the key of the loan amount is changed from amount to loan_amount. How can we decode the data without changing the property name amount of Loan?

Now, update the Loan structure like this:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int

    enum CodingKeys: String, CodingKey {
        case name
        case country
        case use
        case amount = "loan_amount"
    }
}

To define the mapping between the key and the property name, you are required to declare an enum called CodingKeys that has a rawValue of type String and conforms to the CodingKey protocol. In the enum, you define all the property names of your model and their corresponding key in the JSON data. Say, the case amount is defined to map to the key loan_amount. If both the property name and the key of the JSON data are the same, you can omit the assignment.

If you’ve changed the code correctly, you should be able to decode the updated JSON data with the following message found in the console:

Loan(name: "John Davis", country: "Peru", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)

Working with Nested JSON Objects

The JSON data that we have worked on so far has only one level. In reality, the JSON data is usually more complex with multiple levels. Now let’s see how we can decode nested JSON objects.

First, update the json variable like this:

let json = """
{

"name": "John Davis",
"location": {
"country": "Peru",
},
"use": "to buy a new collection of clothes to stock her shop before the holidays.",
"loan_amount": 150

}
"""

We’ve made a minor change to the data by introducing the location key that has a nested JSON object with the nested key country. How can we decode this type of JSON data and retrieve the value of country from the nested object?

Now, modify the Loan structure like this:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int

    enum CodingKeys: String, CodingKey {
        case name
        case country = "location"
        case use
        case amount = "loan_amount"
    }

    enum LocationKeys: String, CodingKey {
        case country
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        name = try values.decode(String.self, forKey: .name)

        let location = try values.nestedContainer(keyedBy: LocationKeys.self, forKey: .country)
        country = try location.decode(String.self, forKey: .country)

        use = try values.decode(String.self, forKey: .use)
        amount = try values.decode(Int.self, forKey: .amount)

    }
}

Similar to what we have done earlier, we have to define an enum CodingKeys. For the case country, we specify to map to the key location. To handle the nested JSON object, we need to define an additional enumeration. In the code above, we name it LocationKeys and declare the case country that matches the key country of the nested object.

Since it is not a direct mapping, we need to implement the initializer of the Decodable protocol to handle the decoding of all properties. In the init method, we first invoke the container method of the decoder with CodingKeys.self to retrieve the data related to the specified coding keys, which are name, location, use and amount.

To decode a specific value, we call the decode method with the specific key (e.g. .name) and the associated type (e.g. String.self). The decoding of the name, use and amount is pretty straightforward. For the country property, the decoding is a little bit tricky. We have to call the nestedContainer method with LocationKeys.self to retrieve the nested JSON object. From the values returned, we further decode the value of country.

That is how you decode JSON data with nested objects. If you’ve followed me correctly, you should see the following message in the console:

Loan(name: "John Davis", country: "Peru", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)

Working with Arrays

In the JSON data returned from Kiva API, it usually comes with more than one loan. Multiple loans are structured in the form of an array. Now, let’s see how to decode an array of JSON objects using Codable.

First, modify the json variable like this:

let json = """

[{
"name": "John Davis",
"location": {
"country": "Paraguay",
},
"use": "to buy a new collection of clothes to stock her shop before the holidays.",
"loan_amount": 150
},
{
"name": "Las Margaritas Group",
"location": {
"country": "Colombia",
},
"use": "to purchase coal in large quantities for resale.",
"loan_amount": 200
}]

"""

To decode the above array into an array of Loan object, all you need to use is to modify the following line of code from:

let loan = try decoder.decode(Loan.self, from: jsonData)

to:

let loans = try decoder.decode([Loan].self, from: jsonData)

As you can see, you just need to specify [Loan].self when decoding the JSON data.

Now that the JSON data is fully utilized, but sometimes you may want to ignore some key/value pairs. Let’s say, we update the json variable like this:

let json = """
{
"paging": {
"page": 1,
"total": 6083,
"page_size": 20,
"pages": 305
},
"loans":
[{
"name": "John Davis",
"location": {
"country": "Paraguay",
},
"use": "to buy a new collection of clothes to stock her shop before the holidays.",
"loan_amount": 150
},
{
"name": "Las Margaritas Group",
"location": {
"country": "Colombia",
},
"use": "to purchase coal in large quantities for resale.",
"loan_amount": 200
}]
}
"""

This JSON data comes with two top-level objects : paging and loans. Apparently, we are only interested in the data related to loans. In this case, how can you decode the array of loans?

To do that, declare another struct named LoanDataStore that also adopts Codable:

struct LoanDataStore: Codable {
    var loans: [Loan]
}

This LoanDataStore only has a loans property that matches the key loans of the JSON data.

Now modify the following line of code from:

let loans = try decoder.decode([Loan].self, from: jsonData)

to:

let loanDataStore = try decoder.decode(LoanDataStore.self, from: jsonData)

The decoder will automatically decode the loans JSON objects and store them into the loans array of LoanDataStore. You can add the following lines of code to verify the content of the array:

for loan in loanDataStore.loans {
    print(loan)
}

The console should have an output like this:

Loan(name: "John Davis", country: "Paraguay", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)
Loan(name: "Las Margaritas Group", country: "Colombia", use: "to purchase coal in large quantities for resale.", amount: 200)

Using Codable in the KivaLoan App

Now that I believe you have some ideas about how to decode JSON using Codable, let’s go back to the KivaLoan project and modify it to use Codable.

Open Loan.swift and replace it with the following code:

import Foundation

struct Loan: Codable {

    var name: String = ""
    var country: String = ""
    var use: String = ""
    var amount: Int = 0

    enum CodingKeys: String, CodingKey {
        case name
        case country = "location"
        case use
        case amount = "loan_amount"
    }

    enum LocationKeys: String, CodingKey {
        case country
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        name = try values.decode(String.self, forKey: .name)

        let location = try values.nestedContainer(keyedBy: LocationKeys.self, forKey: .country)
        country = try location.decode(String.self, forKey: .country)

        use = try values.decode(String.self, forKey: .use)
        amount = try values.decode(Int.self, forKey: .amount)

    }
}


struct LoanDataStore: Codable {
    var loans: [Loan]
}

The code above is exactly the same as the one we developed earlier. The LoanDataStore is designed to store an array of loans.

Next, replace the parseJsonData method of the KivaLoanTableViewController class with the following code:

func parseJsonData(data: Data) -> [Loan] {

    var loans = [Loan]()

    let decoder = JSONDecoder()

    do {
        let loanDataStore = try decoder.decode(LoanDataStore.self, from: data)
        loans = loanDataStore.loans

    } catch {
        print(error)
    }

    return loans
}

Here, we just use the JSONDecoder to decode the JSON data instead of JSONSerialization. I will not go into the code because it is the same as we have just worked on in the Playground project.

Now you’re ready to hit the Run button and test the app in the simulator. Everything should be the same as before. Under the hood, the app now makes use of Codable in Swift 5 to decode JSON.

Editor’s note: This is a sample chapter of the Intermediate iOS 13 Programming with Swift book.

Read next