Matt Ronge

  • Mac apps are harder than iOS apps

    Mon 16 September 2013

    I'm a long time Mac developer who started with Objective-C in 2000 on Mac OS X Public Beta (remember how slow that thing was!) and I've cut my teeth on numerous Mac projects. However, for the last couple years nearly all of my projects have been on iOS. So I was surprised when a recent project brought me back to the Mac and I discovered how finicky Cocoa really is. I was particularly shocked because I always remembered Cocoa as a particularly nice set of APIs. Where things are easy to do on iOS, making customizations can be rage inducing with Cocoa, especially when it comes to custom controls:

    Custom Controls

    At first as a Mac developer, iOS was a little strange for me. I had to resist the urge to subclass and write custom drawing code for controls. On the Mac, to make a custom control you often subclass NSView and then write your own drawing code. Contrast this to iOS where custom controls are more often a combination of multiple views with images and CoreAnimation styles. A great example is how on the iOS you have backgroundColor available on every view, in Cocoa there is no such thing. Instead you need to create a layer backed NSView (which has its own problem's I'll bring up later) or write custom drawing code.

    Apple has embraced customization even further on iOS by providing a great set of APIs for styling the UI. For example, changing the navigation bar style on iOS is supported with a single API call. On the Mac if you want to customize the window title bar, you are going to be in for a world of pain. Apple provides no API for customizing things like the title bar on OS X. Instead, you have to subclass NSWindow and write your own custom drawing code. Getting this drawing code right requires much trial and error (like how do I add back in the close/minimize/expand buttons on the window) .

    Less Resources

    When customizing UI it's often very useful to look at what others have done. iOS has tons of resources because of the pure number of developers, while the Mac has much much less. Much of the information on how to customize Cocoa controls isn't documented. In fact when I was an intern at Apple long ago, when we had to customize Cocoa controls my boss would tell me to go lookup the source code for the control. I could then figure out which methods to override to get the proper style. However as an outside developer you don't have access to these resources. On the iOS side there is a wealth of documentation just on Stack Overflow alone on how to customize and build UI.

    The dreaded NSCell

    I can't talk about customizing UI in Cocoa and not bring up NSCell. iOS developers thankfully have no idea what I'm talking about.

    Cocoa is an old framework, dating back to the early 90's, consequently many aspects don't make sense with current hardware. A perfect example is NSCell. In the 90's it was a performance problem to have lots of NSView's in a window at once. Since NSView's were very resource intensive NSCell's where intendend to be used anywhere you would need lots of views. For example in a table view all the rows would be composed of NSCells instead of bloated NSViews. The NSCells had much less data and knew just how to stamp out the necessary content for the rows. In practice this becomes a problem because you then can't add any control like a button to a table view, it has to be a NSCell.

    iOS (thankfully) did away with this, as it is no longer a performance problem with our modern devices. However this vestige is still hanging around in Cocoa, complicating all our custom controls. Everytime you want to customize something you need to create both a custom view and custom cell. This doesn't sound too bad, but after you've been spoiled by iOS you get frusterated quickly.

    In fairness, Apple is improving this. They've introduced new table views that can use views instead of cells. However customizing things like NSButton is still way too difficult. For example on iOS you can set a layer style to add rounded rects. On the Mac this doesn't work because a cell isn't a view.

    Core Animation

    iOS users have become accustomed to animations and any app without them doesn't feel fresh. So new Mac apps also needs animation, but this is much trickier to do than on iOS. iOS was built from the start with CoreAnimation, making it super easy to add delightful animations. In fact, on iOS UIView is just a thin wrapper over a CoreAnimation layer. On OS X, CoreAnimation was added in much much later as another way to use NSView and has to be turned on. Even worse some components completely fail to work with CoreAnimation, like web views. Other controls haves support for CoreAnimation, but it was hacked in and you are likely to find all sorts of weird edge cases (like try putting a button in a layer backed NSBox, it won't work right). While iOS was built from the ground up with CoreAnimation, on OS X it's always been half baked.

    So many configurations

    The bugginess between controls with and without CoreAnimation is a symptom of an even larger problem: the permutations of configs on the Mac. The Mac simply has more "stuff" than iOS. There is more to worry about, more to test, more to debug. On iOS there is a much smaller number of devices and number of device configurations.

    For example on iOS you need to support both retina and non-retina displays. On the Mac, it is the same situation, but with more possibilities. What happens if the user has one display that is retina and one that isn't? If you drag your app from a retina screen to a non-retina screen will it draw properly? Speaking of screens, what if they have them configured in an odd way, like one screen is to the upper right of their main screen? Does your app handle Mission Control properly? What about drag and drop? Undo? Printing? QuickLook? Spotlight Indexing?

    Bugs

    With the age of the APIs, the amount of "stuff" (check out the NSWindow docs for a taste) and the variety of configurations there are simply more bugs in Cocoa than in UIKit. Throw in things that Mac apps were never designed for like sandboxing and CoreAnimation and you'll find heaps of edge cases. With the intense competition in the mobile space, Apple simply won't devote the resources to fix these outstanding issues on OS X. So it's no surprise that we haven't seen major advances in the desktop APIs, all the growth is in mobile.

    Mac apps are trickier to build, and we aren't likely to see that change in the near future. The APIs have more cruft, there are more hardware configurations, more OS features to worry about, and more hacked on APIs. If you are an iOS developer taking on a Mac project, make sure to give yourself ample time as it's harder than it seems.


  • T-Minus Zero

    Wed 04 January 2012

    After a long hiatus I've decided to revive my blog. To encourage my blogging I've made writing posts as frictionless as possible. I previously used Wordpress and a custom Django app, but both were clunky. My new setup is simpler, just a directory on Dropbox filled with Markdown files. Now I can write in the editor of my choice and as soon as I save the article is live.

    To generate this site I'm using the nifty static blogging engine Pelican. My server runs a copy of Dropbox that syncs the posts. Whenever a file changes in the Dropbox directory, Pelican kicks out an updated version of my site.

    How to setup Dropbox based blogging

    If you'd like a similar setup it's not hard. Once you have a server running the HTTP server of your choice, you'll need to add Pelican, Dropbox and incron. I installed on an Ubuntu 10.04 server but other versions of Linux should be comparable.

    Dropbox

    The first step is getting Dropbox installed. I setup the stock version available from Dropbox's website and found this tutorial to be very helpful. In particular make sure you setup a script in /etc/init.d/ to automatically start the Dropbox daemon. I used the script shown on the linked tutorial for my init.d script.

    You probably want a separate Dropbox account specifically for your blog. Otherwise all your Dropbox files will be synced to your server, a waste of space and a potential security issue. Instead I shamelessly stole the idea of using Dropbox folder sharing from Joe Hewitt. The idea is that you have two Dropbox accounts and then let the blog Dropbox account share its posts folder with your main Dropbox account. Check out the Dropbox web interface for details.

    Pelican

    Pelican is what powers the blog and generates the static output. The install is super simple:

    $ sudo aptitude install python-pip
    $ sudo pip install pelican
    $ sudo pip install markdown
    

    incron

    The final piece is incron which detects change in the Dropbox directory and executes Pelican when it sees a change. Setup is easy but slightly more involved than Dropbox or Pelican:

    $ sudo aptitude install incron
    

    Only users specified in /etc/incron.allow are allowed to use incron so add your user account:

    $ sudo echo $USER >> /etc/incron.allow
    

    Now you need to configure incron which is done like cron:

    $ incrontab -e
    

    Here is what my incrontab file looks like:

    /home/mronge/Dropbox/Blog/Posts IN_MODIFY,IN_CREATE,IN_DELETE,IN_MOVE /home/mronge/update_blog.sh
    

    And then inside update_blog.sh I have:

    pelican /home/mronge/Dropbox/Blog/Posts -t /home/mronge/Dropbox/Blog/Site -o /var/www/mronge -s /home/mronge/pelican_conf.py
    

    which simply runs Pelican and specifies a template, output directory and settings file to use.

    And that's it! You now have a Dropbox powered blog. If you have any questions feel free to contact me.


Page 1 / 1

© 2012 Matt Ronge. All rights reserved.

RSS Feed Powered by Pelican.