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!

⭑⭑⭑⭑⭑

Further Reading