Wednesday, April 29, 2009

iPhone App Development Notes

As I mentioned in my previous post, I'm learning how to write code for the iPhone. While working on my first iPhone app (currently awaiting app store approval!), I came across a number of "gotchas" and things that required a bit of research to resolve. Not to say that they were particularly difficult to resolve; however, they did require a bit more work on my part to find solutions. In case my experience can benefit others (and also to record these things for my own benefit), I'll list them here on my blog. In no particular order:

  • Initialize Settings: When you create an iPhone app, you'll often want to store user preferences in the standard "Settings" location. This is really easy to setup. However, what isn't mentioned is that the settings aren't "initialized" until the first time you access them (this isn't intuitive as they appear as initialized values when you access them the first time in Settings). This one was difficult to diagnose as the application worked sometimes (e.g. - when I had already gone in to check the preferences before starting the app) but not others (e.g. - when I had removed the application and re-installed from scratch on the iPhone and had started up the app before looking at the preferences). Unfortunately, there's no error message and some functionality works and some doesn't, so it's a tricky one to catch. It's easy enough to initialize the preferences, but you need to know that it's something that you have to do (see Apple's AppPrefs sample code. Also, see this post for some generic code that you should be able to reuse easily - but note that there is a memory leak in the example provided in the post. There should be a "[defaults release];" statement at the end of the sample code.).
  • Internet Connectivity: My application needs Internet access in order for some functionality to work properly, so I test for a connection on startup and get a notification if the connection is lost. Apple has some good sample code that demonstrates how to do this (see the Apple Reachability example).
  • Dependent Components in a Picker: I needed to have dependent components in a picker (e.g. - Books displayed in component #1 of the picker and Chapters displayed in component #2 of the same picker - therefore, the Chapters component values needed to vary based on the Book that had been selected). There is a good example of how to setup dependent components in Chapter 7 of the excellent Beginning iPhone Development: Exploring the iPhone SDK book.
  • Re-using Buttons: I have a navigation bar with a button that plays/stops audio playback and I wanted the button description/action/shape to change (e.g. - I have a "Play" button and I wanted it to change to a "Stop" button once "Play" has been pressed). To enable this, you can do something like this:
    [doPlaySoundBarButton release];  
    doPlaySoundBarButton = [[UIBarButtonItem alloc]
    self.navigationItem.rightBarButtonItem = doPlaySoundBarButton;
  • Memory Management: There is no GC on the iPhone, so you need to make certain you aren't leaking memory anywhere. There are some good explanations on how to do this (see here and here) and some good tools. I used a combination of Xcode's "Leaks" in the "Instruments" tool for interactive analysis (see this tutorial for a great set of instructions on using Leaks) and clang for static analysis (see these step-by-step instructions and this user script for running clang from inside of Xcode. Getting clang setup is a bit of a fiddle; however, once you've used it, you'll be happy you did. While Leaks helps you track down the memory leaks you discover during testing, clang catches potential memory leaks in code branches that you may not be testing. It's a really cool tool!
  • Caching web content locally: I access web sites in my app and wanted to give the user the option to cache some content locally on the iPhone (both for performance reasons and so that the content is available when there is no Internet connectivity). To see how to do this, have a look at Apple's URL Cache sample code.
  • File Encodings: If you're saving content to the iPhone that you're reading off the Internet, you may encounter files that are encoded in odd formats. NSStringEncodings only supports a subset of encodings; however, CFStringEncoding supports a more complete set of encodings. So, for example, to read a Hebrew HTML page encoded with "charset=Windows-1255" and write it to a location on the iPhone, you can do something like this:
    NSString *fileString = [[NSString alloc] initWithContentsOfURL:url   
    encoding:CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingWindowsHebrew)
    [fileString writeToFile:filePath
    encoding:CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingWindowsHebrew)
    error:nil]; [fileString release];
  • Disabling Sleep: For certain long-running actions, I wanted to ensure that the iPhone didn't go to sleep while it was performing those activities. To prevent this, do the following:
    [UIApplication sharedApplication].idleTimerDisabled = YES;

    Remember to undo this after your long-running action has completed!
  • Streaming Sound: Most iPhone sound examples effectively "take over" the device while the audio is playing. I wanted to be able to "stream" sound from a remote site in a background thread while the user was scrolling though text in a foreground thread. Luckily, I found Matt Gallagher's excellent article to help me figure this out.
  • Using WebViews: This isn't hard once you've done it; however, I initially had trouble finding good examples. The free "Network I/O" sample chapter from the upcoming iPhone SDK Development book had a good basic example of how to create a WebView that loaded both local and remote URL's. Once I had played around with that a bit, the rest of the WebView stuff that I had to do came pretty easily. One thing that I did that others may need to do is to open cetain types of links using another application (e.g. - in Safari or Mail) rather than in the WebView.
  • Executing JavaScript: After loading a remote HTML page, you can execute JavaScript from Objective C to manipulate the content. I do this mostly for stylistic reasons - for example, to remove any HR elements in an HTML page:
    #define kJavaScriptCmd @"var tagArray = document.getElementsByTagName('hr');for(i = 0; i < tagArray.length; i++){var j=tagArray[i];'none';}" <br/>
    NSString *jsCmd = [NSString stringWithFormat:kJavaScriptCmd];
    [self.webView stringByEvaluatingJavaScriptFromString:jsCmd];
  • Landscape Mode with Tab View Controllers: My app uses a Tab View Controller but the Tab View Controller template supplied by Apple doesn't support landscape mode. There are a number of different solutions to this; however, this one was easy and worked well for me.
  • SCM: Setting up Subversion for my project was pretty easy. However, it was easy because I followed Jeff LeMarche's great step-by-step instructions. Be sure to read the comments. In particular, the one about setting up the Subversion "global ignores" to exclude Xcode settings and other cruft that shouldn't be put into your Subversion repository.
  • Xcode Customizations: I'm a long-time Emacs user and I prefer Emacs to IDE's like Xcode, Eclipse, and Visual Studio. However, I'm still learning iPhone development and developing without Xcode would be too much of a pain at this stage. Also, as IDE's go, Xcode isn't bad (it's definitely better than Eclipse and Visual Studio which I've had to use in the past). For the time being, I've made do with just some customizations to key bindings, color theme, and some user scripts. At some point in the future, I might write a blog post about these customizations. In the meantime, I wanted to digress to "ask the lazyweb" about some Xcode issues:
    1. Digression#1: Has anyone discovered/created a key binding for the "get api documentation for symbol at point" functionality in Xcode (similar to C-h-v and C-h-f in Emacs)? It's available using option-double-mouse-click when you're on a symbol; however, I couldn't find a key binding for it so that I could avoid using the mouse. I could create a user script; however, there must be a key binding for it somewhere!
    2. Digression#2: Another Xcode "nit" is that I have to use the mouse (using the little icon under the "lock" icon in the upper-right-hand side of the view) to split the text editor view, to close the split, and to switch between the splits. I would like to have keybindings similar to C-x-2, C-x-0, and C-x-o in Emacs but I can't find equivalents in Xcode - anyone?
  • Alternative Development Approaches: I initially prototyped my application using PhoneGap (an open source development tool for building fast, easy mobile apps with JavaScript). In combination with PhoneGap, I tried out both the iui and DashCode-specific frameworks for creating iPhone-like UI's. I liked the idea of being able to create an iPhone app using just HTML and JavaScript as I knew how to code in those already. However, after some experimenting, there were certain things that were too slow and certain functionality that wasn't available. In the end, I decided that it was better to "bite the bullet" and learn how to develop iPhone applications using Objective C. I don't regret making the effort; however, if you want to develop an iPhone application that you can easily port to other mobile platforms, you might want to have a look at PhoneGap.

Update-2009-08-11: Under "Xcode Customizations", I listed 2 nits. I've discovered solutions to these:

  1. A key binding for the "get api documentation for symbol at point" functionality in Xcode: I found that I can do this by creating two bindings:
    • Bind C-x C-s to "Select Word"
    • Bind F1 to Help/Find Selected Text in API Reference

    Now I can find the API documentation for the symbol at point by pressing "C-x C-s F1". It would be nicer to have just a single keybinding but...
  2. A key binding to split the text editor view: Cmd-" will split the window vertically, and Alt-Cmd-" splits horizontally (thanks to Chris for pointing this out). You can close the splits with Cmd-'