Loading local JSON file

How to fetch a local JSON file. The articles discusses how you can access the local file system, using Codable and few other methods for prototyping.

In this example, we are going to be loading a JSON file that contains fake metric definitions. We want to keep this data to the local file system, as we don’t know yet the shape of our final implementation.

JSON sample data

Our initial JSON is an array of metrics.

  {
    "metrics":[
      {
        "id":26,"name":"Staffed",
        "channel":"Phone",
        "object":"Application",
        "description":"The number of agents logged on in zero or more agent groups assigned to take calls in a service"
      },
      {
        "id":27,
        "name":"Talking",
        "channel":"Phone",
        "object":"Application",
        "description":"Number of agents currently in the Talking state"
      }
    ]
  }

Lets save it as metrics.json and place it in a folder called data in our xcode project.

  1. File > New > Group > Create the folder name “data”

  2. File > New > File > Scroll down to “Other” > Select “Empty” > then copy and paste the metric.json as shown above

Find IP Address from Wifi Icon

Codable

Then in ViewController.swift we are going to create a Struct that will be the shape of a single metric.

The Swift standard library defines a standardized approach to data encoding and decoding. You adopt this approach by implementing the Encodable and Decodable protocols on your custom types.

Further down you will see an example Decodable for the Array of MetricDefinition.


import UIKit

struct MetricDefinition: Codable {
    var name: String
    var channel: String
    var object: String
    var description: String
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

}

Private Extension

At the bottom of the file we are going to create an extension that will contain our function will load the local JSON file convert it into an Array of MetricDefinition.


import UIKit


struct MetricDefinition: Codable {
    var name: String
    var channel: String
    var object: String
    var description: String
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

}


private extension ViewController {
    // MARK: - Managing the JSON payload

    struct ResponseData: Decodable {
        var metrics: [MetricDefinition]
    }  



    func loadJson(filename fileName: String) -> [MetricDefinition]? {
        if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
            do {
                let data = try Data(contentsOf: url)
                let decoder = JSONDecoder()
                let jsonData = try decoder.decode(ResponseData.self, from: data)
                return jsonData.metrics
            } catch {
                print("error:\(error)")
            }
        }
        return nil
    }  

}

Decodable

The struct ResponseData conforms to Decodable and it as an attribute of metrics which is an array of structs of the type MetricDefinition, which we defined earlier.

// ...

private extension ViewController {
    // MARK: - Managing the JSON payload

    struct ResponseData: Decodable {
        var metrics: [MetricDefinition]
    }  

    func loadJson(filename fileName: String) -> [MetricDefinition]? {
        if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
            do {
                let data = try Data(contentsOf: url)
                let decoder = JSONDecoder()
                let jsonData = try decoder.decode(ResponseData.self, from: data)
                return jsonData.metrics
            } catch {
                print("error:\(error)")
            }
        }
        return nil
    }  

}

Loading local file

The main bundle represents the bundle directory that contains the currently executing code.

The Bundle.main has an instance method for url(forResource:withExtension:) which returns a URL type. You need the URL type for the next step of the process.

Source

// ...

    func loadJson(filename fileName: String) -> [MetricDefinition]? {
        if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
            do {
                let data = try Data(contentsOf: url)
                let decoder = JSONDecoder()
                let jsonData = try decoder.decode(ResponseData.self, from: data)
                return jsonData.metrics
            } catch {
                print("error:\(error)")
            }
        }
        return nil
    }  
}

do catch

Then we use Data(contentsOf: url) to convert data:// URLs to NSData objects. Then create our JSONDecoder() that comes with the standard swift library. And then decode our data and return our ResponseData that we defined earlier.

// ...omitting code for clarity

    func loadJson(filename fileName: String) -> [MetricDefinition]? {
            do {
                let data = try Data(contentsOf: url)
                let decoder = JSONDecoder()
                let jsonData = try decoder.decode(ResponseData.self, from: data)
                return jsonData.metrics
            } catch {
                print("error:\(error)")
            }
        }
        return nil
    }  

}

See it in action

This is the final implementation of our code.

import UIKit

struct MetricDefinition: Codable {
    var name: String
    var channel: String
    var object: String
    var description: String
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // See JSON
        let data = loadJson(filename: "metrics")
        print("Metrics JSON", data!);
    }

}


private extension ViewController {
    // MARK: - Managing the JSON payload

    struct ResponseData: Decodable {
        var metrics: [MetricDefinition]
    }

    func loadJson(filename fileName: String) -> [MetricDefinition]? {
        if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
            do {
                let data = try Data(contentsOf: url)
                let decoder = JSONDecoder()
                let jsonData = try decoder.decode(ResponseData.self, from: data)
                return jsonData.metrics
            } catch {
                print("error:\(error)")
            }
        }
        return nil
    }

}

Within viewDidLoad() we run load the json in the data variable and then print it out to the console.

You should see similar results like below in your console.