RestKit, CocoaPods, and Software Trade-Offs

Ricardo Rodriguez
Nuts & Bolts
Published in
4 min readDec 16, 2015

--

A few weeks ago during our conversations with the folks from the NYC Tech Talent Pipeline and Flatiron School, the topic of CocoaPods was brought up. Having just gone through a major refactoring of our RestKit integration we were more than happy to share our views on CocoaPods and follow up with more details through this short case study.

In short

  1. Adding CocoaPods to your projects does not reduce the complexity of your app; it just hides it temporarily.
  2. A carelessly integrated CocoaPod can cause data corruption, race conditions, memory leaks, and many other headaches.
  3. If you are incapable or unwilling to maintain a CocoaPod yourself, do not use it in the first place.
  4. If the functionality you seek from a CocoaPod is the key technology that powers your app, consider writing it from scratch in order to have full control over implementation, behavior, and performance.
  5. If you and your competitors are all using the same one-size-fits-all CocoaPods, it will be very hard for you to establish a technical competitive advantage.
  6. CocoaPods are great for MVPs, A/B testing of experimental features, and consuming third-party services or APIs. However, you should always wrap them around abstract interfaces.

The Story

A few months ago we started receiving crash reports amounting to 1% of our daily sessions related to errors in files such as:

  • NSManagedObject+MagicalRecord.m
  • RKResponseMapperOperation.m

And cryptic error messages such as:

  • Fatal Exception: NSObjectInaccessibleException
  • EXC_BAD_ACCESS KERN_INVALID_ADDRESS

In addition, we were noticing certain network operations were taking way too long relative to their complexity and the size of the responses.

The Source: RestKit

Behind all this was RestKit, which is one of the few CocoaPods in our project.

RestKit is a modern Objective-C framework for implementing RESTful web services clients on iOS and Mac OS X. It provides a powerful object mapping engine that seamlessly integrates with Core Data and a simple set of networking primitives for mapping HTTP requests and responses built on top of AFNetworking.

Source: https://github.com/RestKit/RestKit

This object mapping engine is a blessing for a rapidly evolving project that needs to be shipped as fast as possible but can prove dangerous since it introduces a generalized (and magical) mapping engine. This engine takes a JSON object’s fields and dynamically maps them into corresponding fields in your model. Change a field in your model, and RestKit automatically maps the JSON response to the new type. It wasn’t a surprise that our network calls were spending most of their time in this mapping phase.

The Problem

Attempts to replicate these issues were extremely hard and whenever a crash occurred, restarting the application would fix the issue. In addition to this, there were reports of application being rendered unusable because of SQLite DB corruption. At the very least, some serious concurrency and race conditions were at play.

Furthermore during this process we also noticed incredibly long running RestKit mapping operations, some of which took over a second to complete.

The Cause

Did flaws in RestKit’s implementation cause these issues? Maybe. Looking through RestKit commit history you can see bug fixes related to the CoreData thread confinement model.

Did a previous developer introduce a careless integration of RestKit? Possible as well.

Only one was thing was certain here: we were not willing to dig any deeper into RestKit’s internal implementation and figure out exactly what was going on. The functionality we needed from RestKit was fairly trivial and we could rewrite our networking code into a cleaner service layer, which would also get rid of other issues with our outdated codebase.

Solution

During one of our iOS Team Meetings we finally decided we had enough of RestKit and that we could get rid of it relatively fast. At Handy, we allocate 20% of engineering resources to engineering-led initiatives, so we were more than happy to do things the right way as part of an engineering sprint. As a solution we introduced an abstract networking service interface in major parts of the application. The implementation sidesteps RestKit but still makes use of external dependencies, which we also plan to deprecate. This abstract interface is common across multiple applications that we work on here at Handy, which will allow us to easily swap the remaining code and quickly converge with the other apps.

The results achieved through this solution include:

  • Greater ROI: By rewriting parts of the code we gained more control of our networking code, which allowed us introduce optimizations specific to our use-case. The alternative would have been to spend resources in a problem that was not core to our business.
  • Reduced Complexity: RestKit does a lot of wonderful but computationally expensive things for developers. Not only did we reduce computational complexity from our application, we also got rid of a black box and replaced it with standard networking code.
  • Better Experience: After the change our crash-free users are trending way closer to 99.9% and the application performs faster than ever. We reduced mapping stages to milliseconds.

Conclusion

Whenever you are deciding to introduce an external library through a CocoaPod or otherwise, remember to communicate clear expectations to your stakeholders. They need to understand that even though your time-to-market will be greatly reduced, you will be exposed to new bugs and performance issues. In many cases you will have limited control over these issues, which will potentially force you to deprecate the libraries down the line.

Looking for mobile, full stack, or platform engineering opportunities at a fun, growing company in Manhattan? You should join us.

--

--