Over the past two weeks I've been working on an update to the Fitocracy application that involves a redesign of the Feed and Notifications interfaces. The previous version of these interfaces are built on top of Core Graphics, all of the drawing is done manually. At the time, these seemed like a good idea; we were still supporting earlier iOS version on earlier devices and there was some merit to architecting those systems that way. In 2014, we support only iOS 6+ and the majority of our users have devices that would benefit far more from GPU accelerated graphics. So for this new version of the feed I moved all the work back to UIKit and, eager to work with something new, I laid out the majority of my views with Auto Layout. As the bulk of the work is beginning to wind down, I've been reflecting on the experience. There are some obvious benefits to having moved the code to Auto Layout.
I think the biggest win overall was a significant decrease in lines of code that would have been dedicated to layout. As the Fitocracy app has gotten older, its codebase has also grown proportionally; it now includes a few hundred thousand lines of code, all of which I am responsible for maintaining. With that in mind, encouraging less new code and finding ways to deprecate old, unnecessary code has become increasingly important to me. Using Auto Layout saved me from writing hundreds of lines of code for the new views I introduced for the Feed updates and I'm very grateful for that.
In a related fashion, Auto Layout, in conjunction with updates to Xcode, has also made it much more pragmatic to strictly adhere to the MVC pattern. Of course, MVC is not perfect by any means, but modularity and standardization are important practices in software development. You don't have to believe in MVC, but you should believe in something. Apple has pushed MVC on iOS developers, but in the past they haven't done a very good job of making it easy to adhere to. In fact, they've arguably made it easier to break it. Layout code in view controllers is an unfortunately ubiquitous practice, one of which I've been guilty on many occasions, even though I know it is wrong. The alternative, creating countless UIView subclasses to manage a view hierarchy, presents a cost that is greater than the adverse effects of breaking MVC. This is especially true when the hierarchy is simple. If one of your overarching goals is to limit the size of your code base, it does not make sense to add two files and thirty lines of code for a new UIView subclass. With Auto Layout, I was able to mostly avoid writing an layout code at all, let alone any in my view controllers. That was pretty great.
Additionally, as of Xcode 5.1 (currently in beta), you can natively create XIBs for UITableViewCell subclasses. This was huge for me. Previously, I was creating my views for them separately in UIView subclasses and loading the XIBs manually and adding them to the table view, which was a mess and involved playing around with various combinations of translatesAutoresizingMaskIntoConstraints amongst the views until Auto Layout decided (seemingly randomly) that my layout was unambiguous. Because I reuse certain parts of the view, I still do have some of them loaded from separate XIBs and have to continue this practice for those views, but for the most part it has gotten significantly easier. The cells that weren't sharing any common views loaded from separate XIBs were a breeze to create and layout. I do however, wish, that Apple would add the ability to specify that UIView in one XIB is to be loaded from a separate file. I recall this being the case when doing Mac development years ago, and I'm curious why it was removed. However, now that XIBs are easily usable once again I imagine this will come in the near future.
So there were certainly several upsides to using Auto Layout for this project. But, of course, there were some aspects that made me want to rip my hair out.
Most notably, the UIView property translatesAutoresizingMaskinToConstraints quickly became my nemesis. The boolean flag, which has an default value of YES unless the view was loaded from a XIB, determines whether the layout engine should turn the autoresizingMask property into a set of NSLayoutConstraints and add them to the superview implicitly. At the surface, this makes some sense; it could potentially facilitate a graceful migration of legacy code whose layout utilizes springs and struts into the modern age. In theory they could peacefully coexist while all new code is moved to Auto Layout. In practice, the property is an absolute nightmare. It makes it incredibly hard to debug issues in your code. This is exacerbated by the fact that it is depended on by certain classes within UIKit, such as UITableViewCell. Additionally, in my experience it has different behaviors between iOS 6 and iOS 7. Indeed, I have recently written code that sets the property on UITableViewCell's contentView different depending on the version of the OS. I suspect I'll have to keep a close eye on this in the future to ensure it doesn't break with software updates.
Alongside this issue is the fact that debugging Auto Layout is a hellish experience. On OS X there is a NSWindow property for displaying constraints visually, so you can see the ambiguity. On iOS we are left with some console output telling you to check if translatesAutoresizingMaskinToConstraints has accidentally screwed up the layout and a list of the constraints that were unable to be satisfied. The list is in the Auto Layout Visual Format, with only the class name of a particular view and it's pointer address as identification. There are libraries that exist to make this process easier, though I haven't tried them yet. I suppose there is room for a Mac app that can render the NSLayoutConstraints visually, though, a la Spark Inspector or Reveal. I'd certainly pay for that.
Thanks for reading.