Migrating an iPhone app off of Three20

I decided to give my EveryMarathon marathon calendar iPhone app a few tweaks and release an updated version while watching a college football bowl game on TV - easy enough, right? I’d developed the app on my old MacBook Pro using Three20, which is an application framework for iOS.

Choices, choices

I didn’t have Three20 on my new MacBook Air, mostly because I wasn’t using it for any current development projects - it’s not maintained any more from what I can tell, as the lead developer moved on to a new framework, Nimbus. I’m not using that either - instead I wrote a boilerplate iOS application template I like to use for new iPhone and iPad apps, especially if they need to talk to Drupal or another REST service.

I had two choices at this point - download and build Three20 as a set of static libraries (major pain in the neck last time I tried, and I think things probably got worse when XCode 4 was released) and tweak the app, or port the whole thing off of Three20 to Apple-standard UIKit code. Luckily, there were a few more football games on TV, so I decided to drop Three20 completely.

Three20 Tables

The one thing I really liked about Three20 was the reasonable handing of table views and data sources - much easier than working with UITableView and its delegate and datasource.  Rewriting all of my table datasource code was a pain in the neck, but it was easy enough to test as I went. I’d commented out all Three20 related code, and removed all references to its header files throughout the app, so I was able to get something running, even if it didn’t do anything, pretty quickly.

Three20 Navigator and URLs

The real mess came from replacing TTNavigator, which I’d initially thought was pretty clever, but as soon as you try and do any special cases, completely falls apart. Basically, the idea is that each controller in your iPhone app maps to a URL, which then you can call from any other controller when a button is pressed, or a table cell is selected, or what have you.

The problem with TTNavigator is that you can’t pass objects in these URLs without some major hacks, but if you write your app using UIKit, you can simply pass an object to a UIViewController in an init method, or set it using a property setter method. Three20 heavily relies on these TTNavigator URLs, and you end up with some weird overloading of default UIKit arguments (such as target in a navigation bar button) to get it all to work. Basically, it’s not pretty under the hood.

Rewriting all of the TTNavigator stuff was pretty straightforward, once I rationalized out my use of arguments in TTURLs - for instance, I was passing Drupal node ids instead of just the object, and then relying on my data singleton to resolve the node ids into objects.

Three20 Tab Bar

Ripping out the Three20 tab bar controller was straightforward - I subclassed UITabBarController instead, replaced the Three20 URLs with instances of the view controllers in navigation controllers, and it all pretty much worked.

TTWebViewController is pretty nice, but I replaced it straight up with SVWebViewController, and it’s basically a drop-in replacement, without relying on a whole bunch of other stuff.

Now that the app doesn’t rely on Three20, creating an iPad version should be pretty straightforward, especially as the app doesn’t have a fancy user interface. Three20 had a couple of branches that maybe supported iPad use, but it seems like the TTNavigator is really holding the app back.

If you have any questions about migrating your Three20 apps over to UIKit, let me know - I didn’t try to migrate the app over to Nimbus, but I don’t think all of the features in Three20 are in Nimbus yet anyway. My biggest problem with Three20 is that once you use it, you’re more or less stuck using it for everything, and it’s tough to bring in third-party or Apple code that doesn’t support it.