Parsing JSON Objects in Swift 2
Over the past New Year’s holiday break, thanks to NatashaTheRobot Newsletter, I had the opportunity to read Failable initializers, revisited by Jesse Squires.
It’s a great article that teaches you how to fragment your code into smaller, nicer, and reusable sub-problems. You should go check it out.
However, there are a few points about the article example, a.k.a. how to parse JSON objects, that I didn’t like:
Architecture Complexity
Developers have to introduce one protocol (JSONParserType
), two structs (one generic JSONValidator
, and one for each model to parse, conforming to the just introduced JSONParserType
protocol) and, finally, one generic function to wrap everything up nicely;
Memory Accesses
Having one JSONValidator
and one JSONParser
means going through all the (model) JSON keys at least twice (as Jesse writes, it’s up to the developer to decide whether to have these two structs merged or not);
The Never-Ending Temptation
In the lights of the previous point, developers might feel authorized to use forced unwrapping inside the JSONParser
struct! 😱
One Protocol to rule them all
While the previous architecture works perfectly, there’s a simpler way to gather all Jesse solution’s advantages in just one, little protocol:
protocol JSONParser {
static func obtainModel(from json: JSON) -> Self?
}
This protocol contains only one static function for your model struct, or final class, that will try to parse the JSON object: if successful, it will return the new, initialized model, otherwise it will return nil.
Even better, thanks to the Swift Guard
and Extension
statements, its implementation is as elegant as it gets, here’s an example:
// Some model
struct MyModel {
let name: String
let number: Int
let date: NSDate
}
// Conforming to the new protocol
extension MyModel: JSONParser {
static func obtainModel(from json: JSON) -> MyModel? {
guard let
// parse json and construct MyModel
else { return nil }
return MyModel(name: name, number: number, date: date)
}
}
If you mind letting your models know where their sources come from, one of Jesse’s strong arguments, you can conform your models to JSONParser in another file and set the extension access level to private (thanks to Swift Studies for the tip).
Finally, this is how you actually use it:
let json = JSON(data: dataFromServer)
if let model = MyModel.obtainModel(from: json) {
// success
} else {
// failure
}
Pretty minimal, isn’t it?
Conclusion
In this context, failable initializers suck. With just a little effort, we can write a much more beautiful and clean code for all our needs.
Happy parsing!