Swift JSON mapping libraries
Today I had to do a little research on the state of JSON mappers in Swift for a new project. I’ve been using a simple JSON library to help me deal with the JSON structure in a safe way, but for this project I had to use something a little more sophisticated like a mapper.
This post describes what I was looking for and the tradeoffs that I was willing to accept. You probably have other requirements so take this text as just one guy’s opinion.
Let me be clear, I’m happy to accept some tradeoffs to use some library that helps me with the mapping form JSON to my model objects but until a certain point.
There are some things that I don’t want in my codebase and if that’s the case I will just do the mapping manually as I’ve done so many times, I know it works perfectly and it’s the most flexible option. You just need a library that allows you to access easily and safely the contents of a JSON.
Now, here are some of the requirements I was looking for.
Don’t pollute my models
The first and big requirement would be to not couple my model with the library, I don’t want to pollute my objects with library code just for sake of creating them easily.
In Objective-C this gets translated to: don’t use any library that requires you to subclass their base model, or things of that style. In Swift (at least the analyzed libraries) this has other forms that I will show.
In an ideal world I just want an object to which I can give a JSON and a “mapping” then it returns me one of my models with all the properties populated or an error if something went wrong.
But that doesn’t seem to be posible right now in Swift due to the limitations of the reflection system, so we are stuck with coupling the mapping with the models themselves for now.
That’s an acceptable tradeoff, but one that has to be minimized.
Transformers
The library has to allow to transform the values coming from the JSON at decoding time in an flexible way. That’s required to deal with dates, URLs or any other kind of basic object.
It’s cool if it deals with some transformations internally already, but it has to give the option to select a custom one for those cases that the API does something different or special.
Support classes
The models have to be accessed from Objective-C land so they have to be Classes. Almost all of the libraries only use struct
as an example, but class
is still there and is important that this is not a limitation.
Errors are important
Dealing with a web API you can only take one thing for sure: errors will happen. So how to deal with them is really important. That’s one of the main points of abstracting this kind of task into a library.
But it’s not only important that the library deals with the errors, but that it gives access to them with clear information on what happened.
ObjectMapper
The first library I tried was ObjectMapper. It’s a nice library and seems to be the most popular but for me was a quick nono.
When I saw the examples I was expecting that my suspicions were not true.
class User: Mappable {
var username: String?
var age: Int?
var weight: Double!
All the properties have to be Optionals (or force-unwrapped optionals, which is worse). This completely breaks the first of the requirements. If I have to use Swift in this way I prefer doing the mapping by hand or just sticking to Objective-C honestly.
Taking a closer look the properties have to be optional because the mapping is done in a method. If we want to remove the optionals we have to provide default values that don’t make much sense.
But the protocol also requires an init
method so I tried moving the mapping there (and that works) to see if I could remove the optional requirements. But even there the compiler gives you an error because the library is actually not returning the values parsed from the JSON and your code is setting them into the property, nope. The property of your object is being passed by reference and being modified internally. That implies that all the properties have to have default values before being mapped.
At this point I was curious if all the libraries had the same problem so I started looking for alternatives.
Genome
Genome was the second library that I tried. At first glance it seemed way better. It can do the mapping at initialization time with a failable initializer and it’s quite flexible.
That’s the first thing I tried. The problem is that Swift requires that init
in classes sets all the values to the properties even before failing. That’s because even when the init
fails the deinit
method is called, so you have to ensure that the object is constructed correctly. Seems like this is a known limitation that would be solved in the future.
Another, minor, thing is that even if you only want to map in the init you still have to provide a sequence(map: Map) throws
method.
Without the limitation of Swift on failable initializers this will probably be a good candidate.
Argo
Argo is the most promising one and also one of the most famous.
It uses a static function in your model that returns a Decoded
object type. This object gives you access to the created model and to the errors. Which is really handy.
In the static function you use a normal init
so it doesn’t couple your models in any way.
The annoying bit is that you need the init
with all the properties of your model as a parameter, which is fine with structs because it’s automatically generated, but not for classes. You should probably have that init
anyway if you want to use your model without depending on JSON (and you should if you are testing at all).
A part from that there is no other caveats. Maybe is too functional with too many operators for some people but I still like it.
It’s also easy to transform values at decode time, you just provide a function of type A -> Decoded
.
func parseURL(URLString: String?) -> Decoded<NSURL?>
Decodable
Decodable is very similar to Argo but, as they say, “without a bizillion functional operators”.
UPDATE: I had a small complain. The mapping fails when there is an error even for Optional properties. But I just missed the try? j => “”
expression! Thanks @fjbelchi for pointing me to this. So no, nothing to complain about, although with Argo the code doesn’t compile because of a type mismatch, so it’s less likely that you will miss it.
Conclusion
There are many others libraries but almost all of them seem to be very similar to Argo or Decodable, at least based on the things that I mentioned here. I stopped at that point because the differences were not a big deal anymore.
Argo or Decodable seem to be the way to go. Or just use any simpler JSON parsing library and do it manually ;)
UPDATE: @merowing_ recommended to check Unbox and it seems pretty cool. It doesn’t require any change on your models, it works with failable initializers and Swift 2 error handling in a nice way, and it accepts value transformers.
Remember that this research has been completely based on what I was looking for. Probably you will have completely different opinions than me and that’s fine. But if you have any comment on what I’ve said, specially if there is something that I missed or misunderstood, I will be listening on Twitter ;)
BTW, I will never get tired of saying it but if you are in Objective-C go and grab KZPropertyMapper.