After writing several iPhone applications I've noticed that there are several common things that should be handled within an application. Something is optional but nice to have (like supporting both portrait and landscape orientations), something is mandatory (like responding to connectivity problems) but they are inherent to any application running on the iPhone. Before you start working on a new application or close to finishing one it is a good idea to check the list and think how you handle the particular design aspect or system state. So here goes the checklist:
Interface Orientation
For many applications it is enough to support just one orientation. Application itself is simpler in this case because you don't have to think about stretching table cells and preventing user input during animation. On the other hand you may be creative and use interface orientation as an option for alternative UI like switching between basic and scientific calculator modes or making fonts larger in landscape mode for better readability.
There are several possible values for orientation defined as
typedef enum {
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;
but most applications really do care only about whether it is just portrait or landscape. You can use macros UIInterfaceOrientationIsPortrait(orientation) and UIInterfaceOrientationIsLandscape(orientation) defined in UIApplication.h to check it.
The right place to handle interface orientation is your UIViewController subclass. By default (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation returns YES only for the portrait orientation so if you want to support both then override it to always return YES. There are other goodies like accessors for header and footer views that slide out while interface is rotating and several callback methods that you may override to get notified about changes in interface orientation. Beware though, that if you support both orientations then you should do it consistently, in every view controller. It's not required by OS or any API but will annoy users otherwise.
If you support only landscape mode then you may add UIInterfaceOrientation property to your Info.plist file to tell the OS the initial interface orientation.
Connectivity Problems
That's what will be checked in your application during acceptance in App Store for sure. And yes, I was guilty of not handling this too. It's not required to handle the absence of connectivity in some particular way but the user should be clearly notified that there is no connection to the desired server. If your application depends on data from some particular service, like iTunes, and this service is not reachable then it should be enough to just show a fullscreen warning image that explains that the application can't function without the service. If service is optional or application does not totally depend on it you may just show error dialog each time connection fails. More hints:
- You can test how your application reacts to connectivity problems by switching on the airplane mode in iPhone preferences.
- There is an undocumented flag, SBUsesNetwork, that you may set in application's Info.plist file to tell the SpringBoard that your application requires network connectivity. If in airplane mode then OS will show a nice system warning dialog with possibility to go to preferences and switch airplane mode off. Although it's undocumented Apple don't mind us using it.
- Another useful (and documented) option is UIRequiresPersistentWiFi. Set this flag if you don't want to loose network after 30 minutes.
- Apple provides sample project named 'Reachability'. There is a useful Reachability class that wraps SCNetworkReachability API.
Saving State On Exit
Normal iPhone applications, like all applications not written by sorcerers working for Apple, can't run in background. Multitasking is simulated by saving the application state on exit and restoring it after the launch. Think of the three steps:
- Define what you will remember: current tab, current search text, etc. Store these values in some application-global dictionary; this is your state.
- Implement applicationWillTerminate:(UIApplication *)application in application delegate to save the state dictionary in a file.
- Implement applicationDidFinishLaunching:(UIApplication *)application in application delegate to read the state dictionary from that file and restore the state.
As far as I know it's not strictly defined by Apple what you should remember. Personally, I think that it is an overkill to save the UI state in most cases, and it may be confusing for a user to relaunch the application after a while and find himself somewhere deep within the navigation hierarchy. What you should save is the user's current data, like search text, because it requires effort to retype.
Low Memory Warning
We all know that we should respond to this hint from the OS by freeing some memory, otherwise it may shutdown our application for good. But it's easy to forget to do so, especially because it is not a visible feature. There are two places where you could react to this situation:
- didReceiveMemoryWarning in view controller
- applicationDidReceiveMemoryWarning:(UIApplication *)application in application delegate; good place to flush global caches
Shake Gesture
It can be the essential part of your application but often it is a convenient alternative to some primary control, typically a button. For example I've used it to complement the 'Refresh' button that updates UI with new data from the server.
To support shake gesture you could intercept motion event in view controller. And don't forget to allow this view controller to become the first responder:
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
[self refreshContent];
}
}
Preferences
You may declare simple preferences for application by creating a Settings bundle (Xcode even has wizard for this). Options defined there will be available via [NSUserDefaults standardUserDefaults] and you don't have to write any code to show them, just edit properties file as described in Application Preferences.
Device Capabilities
If your application relies on specific device features like GPS or camera you may declare this in UIRequiredDeviceCapabilities property of the application bundle. For more details see Device Support. Many other useful properties are documented in Bundle Programming Guide.
Bundle Version
This one is hard to miss because it is required to publish the application in App Store and they do check that it is specified and increased since the last submission. Typically after 1.0 I fix bugs and add new features and right before publishing the application I review the changes and decide how much to bump the version number. You change it in Info.plist file under CFBundleVersion key.