#3 Diary of a Secret App: JSON Parsing

Intro

If you follow me on twitter you must have seen my most recent rant:

And the memory footprint:

The final test when I had everything in place:

In my particular case, Argo was suddenly the bottleneck of the entire app, taking almost 13s on an iPhone4S. Clearly, doing performance tests on a Simulator powered by a MacbookPro is meaningless. May that serve as a lesson.

This left me with a couple of options. Find a different library to handle this1, or do it myself. Since the measurements from the demo project gave me some promising results, I went with the latter.

The demo project, in case you haven't had a look, simply extracts the values from a dictionary, tries to cast it and finally creates the object. It looks like this:

Replacing Argo

I kept two main ideas from Argo:

  • Use Result for handling failures and successful results.2
  • The Decodable protocol.

The new protocol called Mappable looks like this:

You can see that it's pretty similar to the Decodable protocol. mapToJSON(), is used to go the other way around (as expected).

A real example:

With all this in place I ran my measurements and I was baffled. I only got an improvement of 200%. I was expecting at least something in the order of 10 times faster.

Pushing it a bit further

The following tips was what allowed me to push the performance from 2x to almost 11x:

  • I was checking let d : o as? [String: AnyObject] on the first line of every guard (being o an AnyObject). Apparently you don't actually need to. You can assume your o is an [String: AnyObject] and use it as if it was one: guard let s = o["key"] as? String. This was one of the biggest bottlenecks with this new approach.
  • Remove every map, filter, forEach. They are fast, but nothing beats an if and good old for var i = 0, condition; ++i. Yeah the code won't look as good, but performance in this case was more important.
  • Don't use Result's value to get the underlying object. It's faster if you use guard case .Success(let v) = result. Again, it's not by much, but every bit counts.

P.S: The issue can be followed here.
P.P.S: Here you can find a comparison between different parsers.

  1. Genome, Coconut, SwiftyJSON and ObjectMapper

  2. Argo doesn't use Result, but the approach is similar and based on Haskell's Either type.