Swift 4 JSONEncoder and JSONDecoder extensions
With ChaiOne being a digital innovation agency, we are constantly finding ourselves on the bleeding edge of technology. Events such as WWDC becomes a department wide event in our offices. Developers from all different tech stacks can set time aside on their calendars to watch sessions and have discussions afterwards. This type of investment not only keeps ChaiOne up-to-date with what’s new in the tech world, but helps the growth and development of it’s engineers.
My favorite announcement from WWDC 2017 is better JSON parsing with the new classes JSONEncoder
/JSONDecoder
. This alone will clear out entire sections from Swift curated lists like matteocrippa and ioscookies. Today, I’m going to briefly go over what was introduced and show you some must have extensions when parsing JSON in Swift 4.
With that being said, feel free to download the code here so you can follow along.
Before going into Swift 4 code, let’s see how this was done in Swift 3.
do {
if let data = data,
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
// ok now you have a Dictionary<String:Any>, you still have to instantiate objects
}
} catch {
print("Error deserializing JSON: \(error)")
}
This is why frameworks like SwiftJSON were so popular, but the big problem would be the mapping from JSON to your object. This made other frameworks such as unbox very powerful.
Now with Swift 4, let’s say we have the following JSON:
[{
"isActive": true,
"age": 39,
"name": {
"first": "Jan",
"last": "Monroe"
},
"email": "jan.monroe@providco.net"
}]
And here’s are model for it:
struct Person: Codable {
struct Name: Codable {
let first: String
let last: String
}
let isActive: Bool
let age: Int
let name: Name
let email: String
}
Pay attention to that Codable
protocol, that’s pretty important! By having the Person
struct conform to Codable
we are saying this object can both encode and decode itself to an external representation. So what does that mean exactly? How does that help us? Well, that’s where the classes JSONDecoder
and JSONEncoder
come into play. JSONDecoder
and JSONEncoder
handle the mapping from a raw JSON object to your models.
In Swift 4, this is how you can parse JSON out to an object:
guard let file = Bundle.main.url(forResource: "File", withExtension: "json") else {
throw("no file")
}
let data = try Data(contentsOf: file)
let decoder = JSONDecoder()
let people = try decoder.decode([Person].self, from: data)
print(people) // [Person(isActive: true, age: 39, name: .Person.Name(first: "Jan", last: "Monroe"), email: "jan.monroe@providco.net")]
But what if you want to store that object in UserDefaults
or into a file? That’s where JSONEncoder
comes into play:
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let encodedPeople = try encoder.encode(people)
print(String(data: encodedPeople, encoding: .utf8)!) // [{"age":39,"isActive":true,"email":"Jan.Monroe@gmail.com","name":{"first":"Jan","last":"Monroe"}}]
Some of this could definitely become repetitive, plus I don’t like the whole [Person].self
when decoding. Let’s clean this up by moving this code to extensions.
//: Decodable Extension
extension Decodable {
static func decode(data: Data) throws -> Self {
let decoder = JSONDecoder()
return try decoder.decode(Self.self, from: data)
}
}
//: Encodable Extension
extension Encodable {
func encode() throws -> Data {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
return try encoder.encode(self)
}
}
With these new extensions, this turns the above example into:
let people = try [Person].decode(data: data)
print(people) // [Person(isActive: true, age: 39, name: .Person.Name(first: "Jan", last: "Monroe"), email: "jan.monroe@providco.net")]
let jsonData = try people.encode()
print(String(data: jsonData, encoding: .utf8)!) // [{"age":39,"isActive":true,"email":"Jan.Monroe@gmail.com","name":{"first":"Jan","last":"Monroe"}}]
Don’t you feel that reads much clearer? Plus we got rid of the [Person].self
too.
So there you have it, I hope you are just as excited to be working with these new classes as I am. Be sure to download the repo and share this post if you found this helpful!
Want to know more about ChaiOne and it’s culture? Check out our careers page and apply today!