Announcing Stork
Delivering types from JSON like a Stork is the tagline of my most recent pet project, Stork. Stork is a lightweight library written in Swift 4.2 focused on making the flight from JSON to Types as smooth as possible. Stork believes in simplicity, explicitness, and control.
Based on functional programming principles and mildly inspired in Aeson, Stork is the middle sweet between JSON parsers such as Argo - full-fledged but requiring extra dependencies, learning too many operators, and too functional for some - and other parsers that require your types to be mutable, to throw on init, or that take the control away from you by making too many assumptions.
Links
How it works
To go from JSON to types, all you need to do is to state what fields you want to parse. Stork infers their type and parses them for you. To make that possible, your types must follow the FromJson
protocol.
In practice, this means that for a type to be parseable from JSON, it needs to provide a way of being constructed from a JsonValue: string
, number
, bool
, JSON
/[String: Any]
, or [JsonValue]
.
API
Suppose you have a type User
already complying to FromJson
.
You can get Stork to parse you a User
or an array of User
s as follows:
Operators
Stork provides only 4 infix operators to extract and parse a value from a JSON object:
All of these operators share the same arguments: (json: JSON, key: String)
.
In Action
Say that we want to parse User
s from some JSON input, where User
and its nested types are defined as follows:
With Stork, to go from JSON to this model all we need to do is to have these types complying to the FromJson
protocol.
Considerations
Note that failing to extract a required field or failing to parse it to the desired, inferred type, causes Stork to internally fail with an exception, which gets translated into an Optional for you. That might be inconvenient as it gives you no clue as to what field caused Stork to fail to parse.
As I started using Stork
in my own production projects at Kidiyo, my workaround this current behaviour is to write unit/integration tests that verify all the requirements and specifications for all edge cases. As long as the schema - and its variants - of the JSON at hand is well-defined, this should not set you back.
Why
First and foremost, I needed to be able to parse nested types and having worked with proper and well-designed JSON parsers such as Aeson, this sad approach was a no-go to me:
Therefore, I looked into Swift JSON parsers that seemed to get some attention from the iOS community, such as Argo, Gloss, Unbox, and a few others. Argo was clearly - and admittedly - inspired in Aeson (a good thing), but required extra dependencies (a bad thing), so I left it for later as I wanted to see what the other two had to offer. So I tried to make my types parseable using Gloss and Unbox, and I wasn’t happy with the experience. Far from me to be cynical about those projects, but the reality is that I felt impotent using those two libraries. More specifically, the issues I found were:
- a transversal assumption that you always only want to build a type from a JSON object and not from a string, number, boolean or array of values;
- a transversal assumption that RawRepresentable Enum’s must be a direct map from my their JSON counterpart;
- no compile-time guarantees that a (nested) type is parseable, i.e., that it complies to the required protocol to make it so.
At this point, I thought it was fun to write my own parser and see what kind of problems I would face myself. Although Stork is somewhat similar to Argo in design, I intentionally decided not to check how Argo worked, neither externally nor internally, until I had reached Stork’s first working version.
Inspired in Aeson, my idea with Stork was to speak to the community that it targets, where currying and a bazillion of functional operators is not common language.
Finally, I named it Stork
as that was my daughter’s first toy and because I liked the metaphor of having a stork delivering types to those expecting it.