The Descent to Madness (or Cocoa)
I’ve been developing for iOS for a little over 3 years now. I’ve had a few forays into less mobile platforms here and there, with even one very simple OSX app that I wrote a year in that stayed internal to the company (and only crashed some of the time). So when I learned that myself and two other iOS developers would be working on an OSX app for a client, I thought, “I got this; I know iOS, I’m halfway there” and I wasn’t really worried about it. I prepared myself for the project by deep diving into Swift, which was also new to me and by reading Migrating from Cocoa Touch. There are a million blog posts about this (ok, maybe 5) and just as many tutorials on how to make an OSX app in 5 easy steps. And then we started actual development, with designs and requirements in hand, I suddenly realized that Cocoa is actually Cocoa Touch Bizarro Land. Everything looks the same and is named the same, but doesn’t really act the same a great deal of the time. In fact, the distinction between Cocoa and Cocoa Touch is hardly that; Cocoa Touch means Cocoa Touch, Cocoa means Cocoa Touch. Henceforth, for the duration of this blogpost, Cocoa means Cocoa for OSX and Cocoa Touch means Cocoa Touch for iOS. And with that out of the way, let’s talk a little about my struggles in the shadowy wasteland that is Cocoa development.
Obstacles on the Path to Cocoa Greatness
The problem is finding documentation. A great deal of Apple’s documentation references coding samples from almost a decade ago, full of deprecated classes and methods and without a hint of any of the new frameworks added that will soon replace them. The aforementioned migration guide references NSCell as a “lightweight alternative to views” and explains the differences between cell and view-back tableview cells. Luckily for the tableviews in the app, the Table View Programming Guide For Mac clearly recommends using view-backed cells and to avoid NSCell except for displaying legacy code. The class reference has no indication of this recommendation, so it’s a good thing that the programming guide mentioned it. I wasn’t so lucky when I came to need a custom NSButton. The outdated Button Programming Topics document has nothing on the ancient Subclassing NSControl document it links you to in the paltry 2 sentence Subclassing NSButton section; just look at all that Aqua!
Long story short, I fell down an NSCell subclassing rabbit hole. Once a coworker noticed my grunts of irritation and copious facedesking when I got hung up on something, a little searching found him a nice Stack Overflow post about NSCells getting ready for deprecation and maybe I shouldn’t do it that way at all. Wait, what? I slogged through official documentation and didn’t see a whisper of deprecation, nor hint of red strikethrough. Apparently, in WWDC session 204 What’s New in Cocoa, a reference is made of starting to deprecate NSCell. And this entire fiasco of mine could have been avoided by a simple difference between NSButton and UIButton; content insets.
Things in Cocoa Land change quickly and there aren’t enough OSX developers (on the internet) for there to be anywhere near the level of support for iOS from sheer numbers of developers exploring the Cocoa Touch API. It makes sense as a majority of the developers making Apple apps are iOS developers and the ranks of OSX developers seem to only include a sprinkling of enthusiasts and the employees of some big product companies.
Which leads me to my next point: a great deal of Cocoa features seem incomplete compared to iOS. For example, not having content insets on NSButtons. Some of the more custom UI elements we’re doing are forcing us to get really creative with our compromising. We’re not doing anything unheard of or ground-breaking; in fact, we do find blogs mentioning solutions for our problems. The formula for these posts is the following:
Step 1: I see that you’re trying to do something interesting!
Step 2: Here’s the way you probably tried to do it. That doesn’t work because these limitations and these bugs.
Step 3: Here’s a better way to do it. Annnnd here’s why it doesn’t work.
Step 4: So the best solution I can find is method swizzling.
I’m a young developer yet, but even I break out in a cold sweat when I think about needing to use method swizzling in order to animate some views. The level of polish of Cocoa Touch APIs has spoiled me for some time, and while I’m used to developing for less polished platforms, none of them come with the level of constraint that OSX and iOS do. OSX development requires developers to adhere to all of Apple’s rules but without a lot of the great tools that iOS developers get. Interacting with System Preferences proved to be another nightmare for me. Let’s ignore all the crazy stuff I wanted to do (open on the correct pane and tab within that pane). I ended up exporting the System Preferences app header and implementing the Scripting Bridge only to find out the the entire thing is incompatible with Swift because it couldn’t link to all the declarations the System Preferences header promised were there. I clearly still hadn’t learned my lesson about antiquated docs; the Scripting Bridge documentation was last edited in 2008. I could make it work with valueForKeyPath: but that made my skin crawl (as it should). That taught me that I can’t trust the majority of blog posts I find on OSX development because they’re just too old. Lately, I’ve been scrolling to the bottom of the page in any Mac Developer Library docs, extremely wary of anything that hasn’t been updated in more than 2 years. Unfortunately for me, the next stop was AppleScript.
Which was quite weird and dynamic and surprise! No documentation worth noting besides a few examples and an entire internet of people asking how to do a great deal many specific tasks except the one I needed. At this point, I’d already sunk way too much time into this very minor detail and opted to just open System Preferences to the Security & Privacy pane, without doing any fancy tab or checkbox selecting.
It’s not all gloom and doom. There are a lot of really good frameworks and tools in Cocoa that extend the functionality of Cocoa Touch. For example, I am especially grateful that we’re developing now, after Yosemite and some of the great NSViewController changes that made them feel a lot more like UIViewControllers.
The Light at the End of the Scripting Bridge
As we move forward with this project, we’ve been employing some techniques to avoid getting bitten by poor documentation, lack of developer community, and, the worst offender, Cocoa Touch bias.
- Turn off our iOS brain and don’t make assumptions about any framework.
- Don’t trust any documentation from previous releases; things change and sometimes developer guides and class reference pages don’t get updated with anything other than explicit deprecation
- And lastly, contribute! The community won’t grow if we all struggle alone. The next couple blog posts of mine are going to be about some of the other stuff we’ve discovered during the growing pain phase of switching platforms, especially the processes we really had to dig for, in hopes that it will help someone else like me in the future.
UI and screen sizes aside, OSX is a completely different animal than iOS. The assumption that an iOS developer already knows Mac development is a dangerous one for anyone on the project team to have. We can minimize risk while migrating or developing a new OSX app from scratch by avoiding assumptions about Cocoa based on Cocoa Touch knowledge, and avoid outdated documentation and research topics extensively before committing to a solution. We can be a part of the solution by contributing to the community; let your struggles in the dark light the way for the developers that follow you.