Killing our enthusiasm

Dear Steve,

I am an iPhone developer. I love Cocoa Touch—it’s an amazing piece of engineering. I’m having great success with the products I’ve written (one of them even won an ADA at this year’s WWDC.) Sales through iTunes are great and well above my expectations.

And despite of all this, I’m feeling ambivalent about developing new applications for the iPhone.

Of greater concern is that I’m not alone: many of my colleagues are starting to feel the same way. To illustrate, here are some thoughts from people whose work I respect and admire:

Steven Frank – Panic (Transmit/Coda/Unison)

Fraser Speirs – Connected Flow (Exposure)

Wil Shipley – Delicious Monster (Delicious Library)

Brent Simmons – NewsGator (NetNewsWire)

You should also be aware that much of the discontent is being masked by the NDA that’s currently in place. I, and many others, do not want to anger Apple and there are no forums to voice our concerns privately.

As you’ve seen throughout your own career, great engineering is not driven solely by financial rewards. Woz didn’t write Integer BASIC by hand because he thought it would make him rich. Andy Hertzfeld’s and Bill Atkinson’s work on the original Mac wasn’t motivated by greed.

Great developers create amazing software for love as much as money. Take away the artistry and craftsmanship and you’re left with someone pumping out crapware for a weekly paycheck.

I have worked on many different platforms throughout the years: the most important benefit to working on the Mac is the vibrant community of developers. The high quality of Mac applications is due in large part to great developers being able to learn, compete, innovate, and share in a common success. This camaraderie sustains the love for the platform.

I was hoping the same would be true for the iPhone. Sadly, it’s not, and that makes this new platform really hard to love. I’m trying to stay positive in spite of recent developments, but I’m finding my attraction to the iPhone fades a little bit each day. I think it’s important that you know that.

Thanks for your time,

Craig Hockenberry

P.S. As I was writing this essay, Jason Snell and Dan Moren posted some articles at Macworld about the App Store and NDA. The disaffection is starting to spread outside the development community.

Update October 1st, 2008: Thank you.

Lights Off

There was a time when I would have never considered jailbreaking my iPhone. That was a time before I saw Lucas Newman’s and Adam Betts’ groundbreaking application for the iPhone: Lights Off.

It’s a simple game. It’s simple code. And it demonstrated what was possible for the rest of us outside of Cupertino. I was hooked. Big time. Seeing Lights Off at C4[1] was an inspiration for pretty much everything I’ve done on the iPhone since.

As a result, I feel compelled to document this historic piece of software. And what better way to do this than with source code that works on the iPhone 2.0 software. The archive also includes a Jailbreak folder that contains the original source code that worked with version 1.0 of the iPhone OS.

Do not look at this code for tips on how to design iPhone applications correctly. Rather, it is a testament to the curiosity and coding instincts that were required for developing sophisticated software without any documentation or headers. I purposely made as few changes as possible while porting to 2.0: FileMerge can be used to see what’s improved since Jailbreak and you’ll see plenty of compiler warnings.

Since Lights Off is inspired by Tiger Electronic’s game Lights Out, it doesn’t feel right to release this via the App Store. You will have to build and install it yourself; no exceptions. If requested, I will remove this project from the site, so get it now.

Now who will be the first to reach line 212 in puzzles.plist?

Note: If Apple feels this information is crossing over the NDA line, I’ll be removing this essay and the accompanying code. It’s probably a good idea to save it for reference. Hopefully they’ll realize that college students and other developers learning about the iPhone will find it helpful.

Dealing with memory loss: the cleanup

As we saw in the previous post, your view controller’s view can be released at any time because the device needs memory. One of the things you’ll want to look at in your own code is how you cleanup when when the memory warning occurs.

Here’s an interface for a simple view controller class that embeds a web view. The web view is retained because our controller will need to send it messages:

@interface IFWebViewController : UIViewController
{
  UIWebView *webView;
}

// webView is retained because we'll want to do things like this:
//   [self.webView loadRequest:[NSURLRequest requestWithURL:newUrl]]
@property (nonatomic, retain) UIWebView *webView;

@end

We setup and retain the web view when the controller’s loadView method is called:

- (void)loadView
{
  ...

  UIView *contentView = [[[UIView alloc] initWithFrame:applicationFrame] autorelease];
  self.webView = [[[UIWebView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, applicationFrame.size.width, applicationFrame.size.height - 44.0f)] autorelease];
  [self.webView setScalesPageToFit:YES];
  [self.webView setDelegate:self];
  [contentView addSubview:self.webView];

  self.view = contentView;
}

Eventually this method will be called:

- (void)didReceiveMemoryWarning
{
  // default behavior is to release the view if it doesn't have a superview.

  // remember to clean up anything outside of this view's scope, such as
  // data cached in the class instance and other global data.
  [super didReceiveMemoryWarning];
}

The view and its sub-views are automatically released and set to nil by the default implementation. But there’s a problem here: the primary consumer of memory in this view has not been released. The web view is still being retained by the controller.

Since UIViewController doesn’t notify you that the view is being freed, you’re left holding onto a web view that you can’t do anything with since it’s not a part of the view hierarchy.

You can’t solve this problem by using a non-retained reference to the view because you’ll be left with stale reference and a crash if you try to use it. Instead, you’ll want to override the method used to set the view in UIViewController:

- (void)releaseSubviews
{
  self.webView = nil;
}

- (void)setView:(UIView *)view
{
  if (view == nil)
  {
    NSLog(@"setView: releasing subviews");
    [self releaseSubviews];
  }
  [super setView:view];
}

Of course, you’ll also want to release the retained views when the controller is freed:

- (void)dealloc
{
  [self releaseSubviews];
  [super dealloc];
}

When you’re debugging this code, make sure to force the memory warnings in the simulator. You can do this easily with the Hardware > Simulate Memory Warning menu item.

I’ve also found it helpful to poke around in the view controllers instance variables to see what’s allocated, check retain counts, etc. Since you can’t access self.view or [self.view superview] without instantiating a new view instance, you’ll need get at the private property using KVC:

- (void)didReceiveMemoryWarning
{
  // use this to look at view (without creating an instance with self.view)
  NSLog(@"didReceiveMemoryWarning: view = %@, superview = %@", [self valueForKey:@"_view"], [[self valueForKey:@"_view"] superview]);

  [super didReceiveMemoryWarning];
}
(Do not be an idiot and use this technique in production code: there’s nothing to prevent the iPhone SDK developers from renaming this property and causing your app to crash.)
When debugging, it’s also convenient to know when the view is instantiated (or re-instantiated.) Some logging code in viewDidLoad does the trick:
- (void)viewDidLoad
{
  NSLog(@"viewDidLoad: view = %@", self.view);
}

A lot of the complexity in this memory cleanup could be mitigated by UIViewController supporting a unloadView that is called prior to setView:nil in the default didReceiveMemoryWarning. If you agree, feel free to submit a duplicate bug on Radar ID# 5834347.

At present, there are no metrics for memory usage on the device, so there’s a bit of guesswork in determining how much of your view controller’s object graph should be released. If in doubt, free your data and let it be re-instantiated lazily. You’ll find the overall user experience to be much better.

Note: If Apple feels that sharing this information is outside of the bounds of the NDA, I’ll be forced to remove this post. So just add this to your ever growing collection of furbo PDF files.

Dealing with memory loss: the crash

Now that we’re beta testing and able to symbolicate our crash logs, let’s look into one of the crashes that they helped me solve in version 1.0 of Twitterrific.

The crash was happening during the posting of a notification. The backtrace looked something like this:

Program received signal:  “EXC_BAD_ACCESS”.
#0	0x300c87ec in objc_msgSend
#1	0x30675b0e in _nsnote_callback
#2	0x3025380c in _CFXNotificationPostNotification
#3	0x30673f46 in -[NSNotificationCenter postNotificationName:object:userInfo:]
#4	0x3067aa00 in -[NSNotificationCenter postNotificationName:object:]
#5	0x000029fe in -[TwitterrificTouchAppDelegate startTweetRefreshWithMessagesAndFavorites:] at TwitterrificTouchAppDelegate.m:183

The method that initiated the crash was sending IFStartTweetsNotification. This notification allows several views in the UI to reconfigure themselves while the application is refreshing.

One of those views is the toolpad that you see at the bottom of the detailed tweet view. It is a subclass of UIView and is instantiated like this:

- (id)initWithFrame:(CGRect)frame;
{
  self = [super initWithFrame:frame];
  if (self)
  {
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshStarted) name:IFStartTweetsNotification object:nil];
  }
}

When the view was being freed, this code was being used:

- (void)dealloc
{
  ...
  [super dealloc];
}

I had looked at all of my code and was so convinced that this was a UIKit problem that I filed a Radar. But it turns out that it really was my fault: I had overlooked this little bit of code that gets defined when you create a new view controller:

- (void)didReceiveMemoryWarning {
 [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
 // Release anything that's not essential, such as cached data
}

Simple enough to understand: whenever the system determines that it is running low on memory, it starts to call these methods. The default implementation is to free a view if it’s not a part of the current view hierarchy.

With a desktop application, views are never released without an explicit action. (And it’s particularly hard to free them if they are instantiated via a NIB.) As a result, some of them us have gotten a bit sloppy with our view cleanup code. And that sloppiness is what bit me.

When my toolpad was out of view, as it is when you are looking at the main list, and a memory warning was issued, the detail view was released. Because the toolpad was a subview of that view, it was also released.

So what happens when you send a notification to an object that no longer exists? That backtrace I showed you above.

The fix was painfully easy:

- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self name:IFStartTweetsNotification object:nil];
  ...
  [super dealloc];
}

The lesson to be learned here: if you’re doing any kind of registration or allocation in your init methods, make sure that you have corresponding action in your dealloc method. Even if you don’t really need them on the desktop.

In my next essay, I’ll show you how to clean up memory when getting one of these warnings. It’s not as straightforward as you might think.

Note: If Apple feels that sharing this information is outside of the bounds of the NDA, I’ll be forced to remove this post. If you want, pretend like it’s 1995 and print this out instead of bookmarking it.

Beta testing on iPhone 2.0

NOTE: It’s not clear if the information in this essay is covered by the NDA or not. The instructions presented here are either referenced on Apple’s own site or referenced in the publicly available version of Xcode. If requested, I will remove this post.

During Steve Jobs’ WWDC Keynote the announcement of Ad Hoc distribution meant one thing to most of us iPhone developers: beta testing. Unfortunately, the instructions for setting up this beta testing environment are not well documented. I used the following steps to setup a beta test for Twitterrific on the iPhone.

Updating Xcode for beta builds

This is the part of the whole process that eluded me the most. There is a new requirement to add entitlements to your Xcode project. Luckily, someone pointed me to this discussion on Apple’s website. It provides the following steps:

  1. Open up your ad hoc provisioning profile in Text Edit and verify that it contains a “<key>get-task-allow</key>”. If it does not, generate a new provisioning profile using the Program Portal.
  2. In Xcode, select File > New File…
  3. In the dialog source list, under iPhone, select “Code Signing”. Select “Entitlements” then click the Next button.
  4. Name the file “dist.plist” and put it in the root of your Xcode project. Click Finish.
  5. Open dist.plist and uncheck the “get-task-allowed” checkbox. Save the file.
Now that you have setup the entitlements, you’ll want to create a new configuration for your beta builds. I found it easiest to make a copy of my existing App Store distribution configuration and change the code signing setup:
  1. Make sure that you new beta configuration is the Active Configuration. Xcode has some problems if you try to modify Code Signing parameters on a configuration that is not active.
  2. Use Project > Project Settings to open the build settings. Change the “Code Signing Provisioning Profile” to be “Any iPhone OS Device” and then select the name of the provisioning profile you chose in the Program Portal. If you don’t see the name listed, make sure that the .mobileprovision file is located in ~/Library/MobileDevice/Provisioning Profiles and that the “Code Signing Identity” is set to “iPhone Distribution”.
  3. Use Project > Edit Active Target to open the target settings. Change the “Code Signing Entitlements” to be “dist.plist”.
  4. Clean the build and you should then be able to build a beta release.
Xcode can get confused when changing provisioning and other code signing settings, so don’t be afraid to quit and restart to get things synced up.

Adding beta testers

Of course the next step is to sign people up for your beta test. The most important thing to get from them is their device ID. Each iPhone or iPod touch has a unique identifier. You’ll need this to add them to the Ad Hoc distribution.

There are several ways to get this information. iTunes will display the Identifier if you click on the Serial Number after selecting the device in the source list. Pressing Cmd-C (Mac) or Ctrl-C (Windows) will copy the 40 character hex string to the clipboard.

If your beta tester is on a Mac, another solution is to use the iPhone Configuration Utility, which can be downloaded from Apple’s site. After selecting the device, you’ll see the same Identifier field which can be copy and pasted as text.

Finally, you can also use Erica Sadun’s Ad Hoc Helper. Your beta tester can use this application to send you an email with the device’s information directly from the device.

Creating a beta build

After collecting device identifiers from your beta testers, you’ll need to go into the Program Portal and add the devices. I’ve found that the easiest way to manage this is by using the tester’s name as the device name.

Once you’ve finished entering up to 100 device identifiers, you’ll need to add these devices to the your Ad Hoc provisioning profile (Edit > Modify.)

After you’ve finished updating the profile, Download the .mobileprovision file and move it into ~/Library/MobileDevice/Provisioning Profiles.

Now quit and restart Xcode so that it recognizes the new provisioning and perform the following steps:

  1. Select the beta configuration you setup earlier.
  2. Open your project settings with Project > Edit Project Settings.
  3. Update the “Code Signing Provisioning Profile” to use the name of the provisioning you just installed in MobileDevice.
You’ll notice that it’s quite a bit of work to update the provisioning, and since you’re modifying the contents of the project, you’re going to need to checkin .xcodeproj changes to your version control system. My recommendation is to get all your beta testers lined up and do it all at once.

Distributing and installing the beta

At this point, you can do a beta build and it can be run by your beta testers. The only thing left to do is getting them the software.

To be honest, I don’t know how this part works for users that are on Windows. I’m sure it’s possible, but you’re on your own as far as the steps involved.

Updated August 9th, 2008: Several readers have informed me that you can drag both the provisioning profile and the .app folder to the Applications section in iTunes on WIndows. Once there, they will get moved over to the device with the next sync.

For beta testers on the Mac, you’ll need to send them two pieces of data: a ZIP file that contains the .app bundle created by Xcode and a copy of the Ad Hoc .mobileprovision file in the MobileDevice folder.

To install the .mobileprovision file, the beta tester can just drag it onto the iTunes icon in the Dock. After unzipping the .app bundle, your tester can drag the application into Application in the iTunes Library. The next time they sync, the beta version of your application should appear on the device.

If your testers are managing a lot of different applications, they may find that the iPhone Configuration Utility is easier to use than iTunes. It has facilities to manage multiple devices, provisioning profiles and applications.

Hopefully you’ll find this information useful and allows you to increase the overall quality and reliability of your iPhone application. Happy testing!