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.
We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
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.
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
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.
}
}
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
}
}
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
}
}
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
}
}
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
}
}
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.