Today, the iOS team at Oven Bits is excited to announce our first (of hopefully many) open source Swift libraries. The library is called ModelRocket, and it totally rules (just like Swift).
What is ModelRocket?
When Apple announced Swift at WWDC 2014, it became apparent that it was the future of iOS (and now web?) development. Sure, Objective-C may not be going away anytime soon, but it is clearly inferior to Swift, and will eventually fade into irrelevancy where it belongs… But this post isn’t about Objective-C. This is about ModelRocket. :D
A common task in most iOS applications is interacting with a JSON API from a server. And when it comes to interacting with an API, a model is needed to parse the JSON from the server into a meaningful and useable object for the app to use. That’s where ModelRocket comes in. It takes away the grunt work of parsing the JSON response into objects, and allows developers to spend their time on more important tasks.
This idea of JSON object mapping is by no means new. Objective-C has Mantle and even Swift already has some JSON mapping libraries (see Argo, ObjectMapper, and Decodable, to name a few). ModelRocket differs from these implementations in a few key areas:
- JSON serialization
- NSCoding support
- More concise syntax for object definitions
These points will be explained throughout the remainder of this post, but for now, let’s take a look at the basics of ModelRocket and how it works.
The following code demonstrates a basic class definition for an object of type
Vehicle. This is all that is needed to parse JSON from a server:
With six lines of code, you have an implementation for a
Vehicle object that includes built-in NSCoding support and JSON serialization. NSCoding support means that the object can be persisted to the device (using NSKeyed[Archiver|Unarchiver]) without requiring developers to implement
encodeWithCoder(aCoder:). NSKeyed[Archiver|Unarchiver] isn’t always the best choice for data persistence, but ModelRocket objects support it out of the box for when you need it. By the way, we hope to add support for Core Data and/or Realm at some point, so be on the lookout for that.
How does it work?
ModelRocket uses many Swift features, including Generics, Protocols, and Reflection. Since Generics and Protocols have been written about extensively since Swift was released, we’ll focus primarily on the Reflection aspect, since it’s less well known.
When ModelRocket was first written, it did not actually use Reflection. Instead, it required an additional array of
PropertyDescription objects to let ModelRocket known which properties to parse. So using the
Vehicle example from above, the class definition used to look like this:
While still fairly concise, this
JSONMappings array was an unfortunate “necessity” which added complexity to ModelRocket class definitions. If the developer forgot to included one of their properties in this list, it would not be parsed, and there was no way for them to know, until they realized that the property kept returning a nil value. This was simply the way things were with ModelRocket until one quiet May afternoon, one of our iOS developers, John Daub, brought up the idea of using Reflection to remove the need for this
What is Reflection?
According to Wikipedia:
In computer science, reflection is the ability of a computer program to examine and modify the structure and behavior (specifically the values, meta-data, properties and functions) of the program at runtime.
A lesser known feature of the Swift Standard Library is support for Reflection. ModelRocket uses the Swift Reflection API to examine an object’s properties and parse the correct JSON into each property. So instead of requiring
JSONMappings to inform ModelRocket of which properties it should parse, ModelRocket uses Reflection to determine these properties automatically. For more info on using Reflection in Swift, check out NSHipster’s post.
The first thing you’ll notice while using ModelRocket, is the fact that every
PropertyDictionary type contains a
values property. As the name implies, this is where the underlying value(s) for each property reside. In other words, using our
Vehicle example from above, if you wanted to retrieve the string value of the vehicle’s make, you would use this syntax:
In my estimation, this is the only thing holding ModelRocket back from being insanely great, however, there are a couple alternatives, if you find yourself disliking the
First, applying an empty subscript to a
Property type will return its underlying value. For example:
Another option is to use a private
Property type and a public property for the value. For example:
This second option is especially nice, since it masks the underlying implementation of ModelRocket away from the rest of the app, allowing developers to use ModelRocket in their current apps, without adding
Since we started using ModelRocket at Oven Bits, we have found it to be very useful in handling the model layer in our iOS applications. You can check out the repo on Github for more detailed information. ModelRocket supports Carthage and Cocoapods, is sufficiently unit tested, and uses Travis for continuous integration. And when Apple officially releases Swift 2 in a few weeks, we’ll support all the new goodies that come with it. Our hope is that others will find it just as useful as we do and contribute to making it even better. Swift on!