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.
-
File > New > Group > Create the folder name “data”
-
File > New > File > Scroll down to “Other” > Select “Empty” > then copy and paste the metric.json as shown above
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.
// ...
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.
