Fo' Swizzle

It is said that necessity is the mother of invention. Granted, I haven’t invented anything, however necessity did drive my latest solution.

Vendors – PIA

While working on a project for work I have the unfortunate requirement of using one of our vendor’s iOS frameworks that is horribly written and documented. From day one I discovered bugs and inconsistencies with how it is implemented. For one case I ended up reverse engineering what they were doing and sent them the snippet they should add in to fix the problem.

Not Wasting Time

Getting vendor to make changes that I needed, and consequently, the company, is about as quick as standing in line at your local DMV. I had to move the project along as fast as possible. During the early stages of the project I picked up a copy Graham Lee’s Test Driven iOS Development and took his advice on how to make my unit testing much more efficient with better coverage…so I started writing as many mock objects as I could for unit testing. In order to handle future test cases I created a MockObjectDataManager, but I found myself writing a lot of if/else statements throughout the app itself to handle switching between dev/test/stage/production web services vs. mockobjects. This was not good.

What I needed to do was setup the app so that any other developer, including myself, only had to write calls to the vendor’s framework, but could switch easily to using mockobjects when appropriate.

Method Swizzling to the rescue!

Fo’ Swizzle

The What

Unlike creating a category method with the same name as the original method (effectively replacing the original method), MethodSwizzling lets your replacement method make use of the original method, almost like subclassing.

Source: CocoaDev

Danger Will Robinson, Danger

I have read and seen developers using this technique, but it usually followed up with disclaimers such as:

  • unstable
  • experimental only
  • use at your own risk

Because of this general attitude I had seen I shy’d away from every really using this runtime feature. While I was foolish early on in my iOS development career to not learn more about it, using it without serious thought and consideration would probably proved distasterous. Granted, swizzling does allow a developer to swap methods for their own implemenations, sed developer has to make a lot of assumptions about the implemenation of the original method. If that implemenation changes the likelihood for your replacement method to break increases.

Necessity

As stated earlier myself and my team needed the ability to rely on the vendor framework for communication to and from the web services, but also needed the ability to writing unit tests and CRUD mockobjects WITHOUT cluttering the app code base.

First, I recreated the json response from each endpoint and saved those into static files. Second, I rewrote the mock object manager to access and cache the models asynchronously, with better error handling. Third, I added in a preprocessor macro to flag whether or not the swizzled method should be used. Finally, added in a convience method to handle swizzle.

I hit Cmd+r and was off to the races. The app crashed. Had I been over confident in my ability to leverage the almighty runtime. Well, no…I just needed to pay attention to how classes are loaded. As I stated earlier, my mockobject data manager loads the .json files asynchronously. When the swizzled method was called the dispatch_queue was NULL and resulting in a BAD_ACCESS error.

iFixit

The first attempt was to check for existence of the queue in the loadLoginResponse method:

if (!_fileQueue) { _fileQueue = dispatch_queue_create(fileQueueName, 0); } But wait…I had already instantiated _fileQueue in the sharedManager class method. Didn’t seem very reusble. As to not be out done this far into it I remembered the lifecycle of how/when classes are loaded and most importantly which the order of the meta methods. I moved the property and check from both the sharedManager and loadLoginResponse methods to + (void)initialize. Setting the USEMOCKOBJECTS to 1 and rerunning the app I successfully loaded my mock objects.

Lessons Learned

The most important lesson learned is to not be afraid to experiment with the more advanced features of Objective-C. When used correctly they provide a lot of flexibility and benefits to development and testing. My app is now setup to give any developer the ability to test against any live enviornment, as well as, test out new/existing features without changing a 3rd party framework or cluttering the codebase with a bunch of unnecessary “if/else” statements.

A great example of using these types of techniques is PonyDebugger from a little company called Square.

Get the Gist

gist.github.com/3762103

Fun Times with Objective-C Runtime

Good reusable solutions to problems usually come out of necessity rather than luck or leisure.  Over the past 18 months I was always faced with problems of how to simplify the data integrity, security, and aggregation of multiple datasources, usually in big organizations that would then be fed in an iOS app.

The first solution to this issue is to create the "man in the middle".  Essentially a proxy/web services layer that acts as the gateway to all the necessary model objects.  Utilizing tools such as Gearman, CouchDB, MySQL, PHP, Python, Memcache and a little bit of Apache to make everything complete I was able to deploy a lightweight standard response to the iOS app.  As requests from various business groups would come in I found myself taking more and more out of the app and putting that logic into the services layer. Instead of having to rely on the app to handle security, data integrity, computational analysis, image manipulation, etc. I pushed that onto a light weight stack server side that could be scaled easily and mainainted without having to revision the app for basic model object changes.  The added benefits that I saw later on were that I could adapt the response based upon the request…(Android, iOS, mobile web, desktop browser, etc.)

Now that I was concentrating more on feature sets, bug fixes and UI enhancements I was still stuck with having to manage various model classes within the app.  This was a serious pain point to me. I wanted to be able to consolidate the various list and detail controllers that I had down to one instance of each.

Objective-C runtime to the rescue.

Let me be very clear that objective-c runtime is VERY powerful and not for the faint of heart, but when used effectively can be quite useful.  The approach that I took was as follow. 

Use the existing "standard" json format from a web services layer and restructure the "responseObject" so that it gave meta information about the model name, properties for class, which properties should be displayed on the list page and detail page.  By structuring the json  (xml, plist, etc) play load this way I now can push changes to model object and display anytime I want.

In the github project I use an "Employee" object as the example. The app should be able to show a list of employees (title and subtitle) and then a detail page for each employee that would display up to 3 properties.

I have included another json file for "Events" just so that I can show the flexibility. The same list/detail view controllers are now reused for two different objects.

The (current) limitations.
  1. There is an assumption that your list view will use the subtitle uitableviewcell enum type.
  2. Parsing and creation of classes doesn't take into account types other than nsstrings. I have an idea to remedy this, just haven't implemented yet.
  3. What about images?  Most list views have an image associated with it.  What I am thinking about it having a flag set in the properties specifically for images so that they can be loaded asynchronously.
I will be adding in these features.  I am still getting my head wrapped around some of the advanced features of runtime. Below are the links to resources that I used in my research.

Posted on slideshare is the presentation that I gave. It details the philosophical and practical importance of having an SOA that is similar to what I described.  It was heavily inspired by the presentation written on the subject of NPR's SOA.

Presentation References: