Advanced App Tricks

Many app-related tasks depend on the type of app you are trying to create. This chapter shows you how to implement some of the common behaviors found in iOS apps.

Configuring Your App to Support iPhone 5

Apps linked against iOS 6 and later should be prepared to support the larger screen size of iPhone 5 and iPod touch (5th generation) devices. To support the larger screen in your code properly, never make assumptions about the current device’s screen size. Instead, always retrieve the size of a screen, window, or view dynamically and use that size information to configure your interface. You should also build your user interface using view-based constraints, which make it much easier to manage the changes to your view hierarchies at runtime.

To let the system know that your app supports the iPhone 5 screen size, include a properly named launch image in your app’s bundle. At runtime, the system looks for a launch image whose name contains the -568h modifier. If such an image is present, the system assumes that your app supports the iPhone 5 explicitly and runs it in fullscreen mode. If such an image is not present, the system runs your app with black bars above and below your app’s content on devices with the larger screen; it also reports the screen size of your app as 320 by 480 points so that your app’s screen-based calculations continue to be correct.

For more information about specifying the launch images for your app, see “App Launch (Default) Images.”

Creating a Universal App

A universal app is a single app that is optimized for iPhone, iPod touch, and iPad devices. Providing a single binary that adapts to the current device offers the best user experience but, of course, involves extra work on your part. Because of the differences in device screen sizes, most of your window, view, and view controller code for iPad is likely to be very different from the code for iPhone and iPod touch. In addition, there are things you must do to ensure your app runs correctly on each device type.

Xcode provides built-in support for configuring universal apps. When you create a new project, you can select whether you want to create a device-specific project or a universal project. After you create your project, you can change the supported set of devices for your app target using the Summary pane. When changing from a single-device project to a universal project, you must fill in the information for the device type for which you are adding support.

The following sections highlight the changes you must make to an existing app to ensure that it runs smoothly on any type of device.

Updating Your Info.plist Settings

Most of the existing keys in a universal app’s Info.plist file should remain the same. However, for any keys that require different values on iPhone versus iPad devices, you can add device modifiers to the key name. When reading the keys of your Info.plist file, the system interprets each key using the following format:

key_root-<platform>~<device>

In this format, the key_root portion represents the original name of the key. The <platform> and <device> portions are both optional endings that you can use for keys that are specific to a platform or device. For apps that run only on iOS, you can omit the platform string. (The iphoneos platform string is used to distinguish apps written for iOS from those written for Mac OS X.) To apply a key to a specific device, use one of the following values:

  • iphone—The key applies to iPhone devices.

  • ipod—The key applies to iPod touch devices.

  • ipad—The key applies to iPad devices.

For example, to indicate that you want your app to launch in a portrait orientation on iPhone and iPod touch devices but in landscape-right on iPad, you would configure your Info.plist with the following keys:

<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationPortrait</string>
<key>UIInterfaceOrientation~ipad</key>
<string>UIInterfaceOrientationLandscapeRight</string>

Notice that in the preceding example, there is an iPad-specific key and a default key without any device modifiers. Continue to use the default key to specify the most common (or default) value and add a specific version with a device-specific modifier when you need to change that value. This guarantees that there is always a value available for the system to examine. For example, if you were to replace the default key with an iPhone-specific and iPad-specific version of the UIInterfaceOrientation key, the system would not know the preferred starting orientation for iPod devices.

For more information about the keys you can include in your Info.plist file, see Information Property List Key Reference

Implementing Your View Controllers and Views

The largest amount of effort that goes into creating universal apps is designing your user interface. Because of the different screen sizes, apps often need completely separate versions of their interface for each device idiom. This means creating new view hierarchies but might also mean creating completely different view controller objects to manage those views.

For views, the main modification is to redesign your view hierarchies to support the larger screen. Simply scaling existing views may work but often yields poor results. Your new interface should make use of the available space and take advantage of new interface elements where appropriate. Doing so is better because it results in an interface that feels more natural to the user, and does not just feel like an iPhone app on a larger screen.

For view controllers, follow these guidelines:

  • Consider defining separate view controller classes for iPhone and iPad devices. Using separate view controllers is often easier than trying to create one view controller that supports both platforms. If there is a significant amount of shared code, you could always put the shared code in a base class and then implement custom subclasses to address device-specific issues.

  • If you use a single view controller class for both platforms, your code must support both iPhone and iPad screen sizes. (For an app that uses nib files, this might mean choosing which nib file to load based on the current device idiom.) Similarly, your view controller code must be able to handle differences between the two platforms.

For views, follow these guidelines:

  • Consider using separate sets of views for iPhone and iPad devices. For custom views, this means defining different versions of your class for each device.

  • If you choose to use the same custom view for both devices, make sure your drawRect: and layoutSubviews methods especially work properly on both devices.

For information about the view controllers you can use in your apps, see View Controller Programming Guide for iOS.

Updating Your Resource Files

Because resource files are often used to implement portions of your app’s user interface, you need to make the following changes:

  • In addition to the Default.png file displayed when your app launches on iPhone devices, you must add new launch images for iPad devices as described in “Providing Launch Images for Different Orientations.”

  • If you use images, you may need to add larger (or higher-resolution) versions to support iPad devices.

  • If you use storyboard or nib files, you need to provide a new set of files for iPad devices.

  • You must size your app icons appropriately for iPad, as described in “App Icons.”

When using different resource files for each platform, you can conditionally load those resources just as you would conditionally execute code. For more information about how to use runtime checks, see “Using Runtime Checks to Create Conditional Code Paths.”

Using Runtime Checks to Create Conditional Code Paths

If your code needs to follow a different path depending on the underlying device type, use the userInterfaceIdiom property of UIDevice to determine which path to take. This property provides an indication of the style of interface to create: iPad or iPhone. Because this property is available only in iOS 3.2 and later, apps that support earlier versions of iOS need to check for the availability of this property before accessing it. Of course, the simplest way to check this property is to use the UI_USER_INTERFACE_IDIOM macro, which performs the necessary runtime checks for you.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
     // The device is an iPad running iOS 3.2 or later.
}
else {
     // The device is an iPhone or iPod touch.
}

Supporting Multiple Versions of iOS

Any app that supports a range of iOS versions must use runtime checks to prevent the use of newer APIs on older versions of iOS that do not support them. For example, if you build your app using new features in iOS 6 but your app still supports iOS 5, runtime checks allow you to use recently introduced features when they are available and to follow alternate code paths when they are not. Failure to include such checks will cause your app to crash when it tries to use new symbols that are not available on the older operating system.

There are several types of checks that you can make:

For more information and examples of how to write code that supports multiple deployment targets, see SDK Compatibility Guide.

Launching in Landscape Mode

Apps that uses only landscape orientations for their interface must explicitly ask the system to launch the app in that orientation. Normally, apps launch in portrait mode and rotate their interface to match the device orientation as needed. For apps that support both portrait and landscape orientations, always configure your views for portrait mode and then let your view controllers handle any rotations. If, however, your app supports landscape but not portrait orientations, perform the following tasks to make it launch in landscape mode initially:

The UIInterfaceOrientation key in the Info.plist file tells iOS that it should configure the orientation of the app status bar (if one is displayed) as well as the orientation of views managed by any view controllers at launch time. In iOS 2.1 and later, view controllers respect this key and set their view’s initial orientation to match. Using this key is equivalent to calling the setStatusBarOrientation:animated: method of UIApplication early in the execution of your applicationDidFinishLaunching: method.

Installing App-Specific Data Files at First Launch

You can use your app’s first launch cycle to set up any data or configuration files required to run. App-specific data files should be created in the Library/Application Support/<bundleID>/ directory of your app sandbox, where <bundleID> is your app’s bundle identifier. You can further subdivide this directory to organize your data files as needed. You can also create files in other directories, such as to your app’s iCloud container directory or to the local Documents directory, depending on your needs.

If your app’s bundle contains data files that you plan to modify, you must copy those files out of the app bundle and modify the copies. You must not modify any files inside your app bundle. Because iOS apps are code signed, modifying files inside your app bundle invalidates your app’s signature and prevents your app from launching in the future. Copying those files to the Application Support directory (or another writable directory in your sandbox) and modifying them there is the only way to use such files safely.

For more information about the directories of the iOS app sandbox and the proper location for files, see File System Programming Guide.

Protecting Data Using On-Disk Encryption

In iOS 4 and later, apps can use the data protection feature to add a level of security to their on-disk data. Data protection uses the built-in encryption hardware present on specific devices (such as the iPhone 3GS and iPhone 4) to store files in an encrypted format on disk. While the user’s device is locked, protected files are inaccessible even to the app that created them. The user must explicitly unlock the device (by entering the appropriate passcode) at least once before your app can access one of its protected files.

Data protection is available on most iOS devices and is subject to the following requirements:

To protect a file, your app must add an attribute to the file indicating the desired level of protection. Add this attribute using either the NSData class or the NSFileManager class. When writing new files, you can use the writeToFile:options:error: method of NSData with the appropriate protection value as one of the write options. For existing files, you can use the setAttributes:ofItemAtPath:error: method of NSFileManager to set or change the value of the NSFileProtectionKey. When using these methods, your app can specify one of the following protection levels for the file:

If you protect a file, your app must be prepared to lose access to that file. When complete file protection is enabled, even your app loses the ability to read and write the file’s contents when the user locks the device. Your app has several options for tracking when access to protected files might change, though:

For new files, it is recommended that you enable data protection before writing any data to them. If you are using the writeToFile:options:error: method to write the contents of an NSData object to disk, this happens automatically. For existing files, adding data protection replaces an unprotected file with a new protected version.

Tips for Developing a VoIP App

A Voice over Internet Protocol (VoIP) app allows the user to make phone calls using an Internet connection instead of the device’s cellular service. Such an app needs to maintain a persistent network connection to its associated service so that it can receive incoming calls and other relevant data. Rather than keep VoIP apps awake all the time, the system allows them to be suspended and provides facilities for monitoring their sockets for them. When incoming traffic is detected, the system wakes up the VoIP app and returns control of its sockets to it.

There are several requirements for implementing a VoIP app:

  1. Add the UIBackgroundModes key to your app’s Info.plist file. Set the value of this key to an array that includes the voip string.

  2. Configure one of the app’s sockets for VoIP usage.

  3. Before moving to the background, call the setKeepAliveTimeout:handler: method to install a handler to be executed periodically. Your app can use this handler to maintain its service connection.

  4. Configure your audio session to handle transitions to and from active use.

  5. To ensure a better user experience on iPhone, use the Core Telephony framework to adjust your behavior in relation to cell-based phone calls; see Core Telephony Framework Reference.

  6. To ensure good performance for your VoIP app, use the System Configuration framework to detect network changes and allow your app to sleep as much as possible.

Including the voip value in the UIBackgroundModes key lets the system know that it should allow the app to run in the background as needed to manage its network sockets. This key also permits your app to play background audio (although including the audio value for the UIBackgroundModes key is still encouraged). An app with this key is also relaunched in the background immediately after system boot to ensure that the VoIP services are always available. For more information about the UIBackgroundModes key, see Information Property List Key Reference.

Configuring Sockets for VoIP Usage

In order for your app to maintain a persistent connection while it is in the background, you must tag your app’s main communication socket specifically for VoIP usage. Tagging this socket tells the system that it should take over management of the socket when your app is suspended. The handoff itself is totally transparent to your app. And when new data arrives on the socket, the system wakes up the app and returns control of the socket so that the app can process the incoming data.

You need to tag only the socket you use for communicating with your VoIP service. This is the socket you use to receive incoming calls or other data relevant to maintaining your VoIP service connection. Upon receipt of incoming data, the handler for this socket needs to decide what to do. For an incoming call, you likely want to post a local notification to alert the user to the call. For other noncritical data, though, you might just process the data quietly and allow the system to put your app back into the suspended state.

In iOS, most sockets are managed using streams or other high-level constructs. To configure a socket for VoIP usage, the only thing you have to do beyond the normal configuration is add a special key that tags the interface as being associated with a VoIP service. Table 6-1 lists the stream interfaces and the configuration for each.

Table 6-1  Configuring stream interfaces for VoIP usage

Interface

Configuration

NSInputStream and NSOutputStream

For Cocoa streams, use the setProperty:forKey: method to add the NSStreamNetworkServiceType property to the stream. The value of this property should be set to NSStreamNetworkServiceTypeVoIP.

NSURLRequest

When using the URL loading system, use the setNetworkServiceType: method of your NSMutableURLRequest object to set the network service type of the request. The service type should be set to NSURLNetworkServiceTypeVoIP.

CFReadStreamRef and CFWriteStreamRef

For Core Foundation streams, use the CFReadStreamSetProperty or CFWriteStreamSetProperty function to add the kCFStreamNetworkServiceType property to the stream. The value for this property should be set to kCFStreamNetworkServiceTypeVoIP.

Because VoIP apps need to stay running in order to receive incoming calls, the system automatically relaunches the app if it exits with a nonzero exit code. (This type of exit could happen when there is memory pressure and your app is terminated as a result.) However, terminating the app also releases all of its sockets, including the one used to maintain the VoIP service connection. Therefore, when the app is launched, it always needs to create its sockets from scratch.

For more information about configuring Cocoa stream objects, see Stream Programming Guide. For information about using URL requests, see URL Loading System Programming Guide. And for information about configuring streams using the CFNetwork interfaces, see CFNetwork Programming Guide.

Installing a Keep-Alive Handler

To prevent the loss of its connection, a VoIP app typically needs to wake up periodically and check in with its server. To facilitate this behavior, iOS lets you install a special handler using the setKeepAliveTimeout:handler: method of UIApplication. You typically install this handler in the applicationDidEnterBackground: method of your app delegate. Once installed, the system calls your handler at least once before the timeout interval expires, waking up your app as needed to do so.

Your keep-alive handler executes in the background and should return as quickly as possible. Handlers are given a maximum of 10 seconds to perform any needed tasks and return. If a handler has not returned after 10 seconds, or has not requested extra execution time before that interval expires, the system suspends the app.

When installing your handler, specify the largest timeout value that is practical for your app’s needs. The minimum allowable interval for running your handler is 600 seconds, and attempting to install a handler with a smaller timeout value will fail. Although the system promises to call your handler block before the timeout value expires, it does not guarantee the exact call time. To improve battery life, the system typically groups the execution of your handler with other periodic system tasks, thereby processing all tasks in one quick burst. As a result, your handler code must be prepared to run earlier than the actual timeout period you specified.

Configuring Your App’s Audio Session

As with any background audio app, the audio session for a VoIP app must be configured properly to ensure the app works smoothly with other audio-based apps. Because audio playback and recording for a VoIP app are not used all the time, it is especially important that you create and configure your app’s audio session object only when it is needed. For example, you would create the audio session to notify the user of an incoming call or while the user was actually on a call. As soon as the call ends, you would then remove strong references to the audio session and give other audio apps the opportunity to play their audio.

For information about how to configure and manage an audio session for a VoIP app, see Audio Session Programming Guide.

Using the Reachability Interfaces to Improve the User Experience

Because VoIP apps rely heavily on the network, they should use the reachability interfaces of the System Configuration framework to track network availability and adjust their behavior accordingly. The reachability interfaces allow an app to be notified whenever network conditions change. For example, a VoIP app could close its network connections when the network becomes unavailable and recreate them when it becomes available again. The app could also use those kinds of changes to keep the user apprised about the state of the VoIP connection.

To use the reachability interfaces, you must register a callback function with the framework and use it to track changes. To register a callback function:

  1. Create a SCNetworkReachabilityRef structure for your target remote host.

  2. Assign a callback function to your structure (using the SCNetworkReachabilitySetCallback function) that processes changes in your target’s reachability status.

  3. Add that target to an active run loop of your app (such as the main run loop) using the SCNetworkReachabilityScheduleWithRunLoop function.

Adjusting your app’s behavior based on the availability of the network can also help improve the battery life of the underlying device. Letting the system track the network changes means that your app can let itself go to sleep more often.

For more information about the reachability interfaces, see System Configuration Framework Reference.

Communicating with Other Apps

Apps that support custom URL schemes can use those schemes to receive messages. Some apps use URL schemes to initiate specific requests. For example, an app that wants to show an address in the Maps app can use a URL to launch that app and display the address. You can implement your own URL schemes to facilitate similar types of communications in your apps.

Apple provides built-in support for the http, mailto, tel, and sms URL schemes. It also supports http–based URLs targeted at the Maps, YouTube, and iPod apps. The handlers for these schemes are fixed and cannot be changed. If your URL type includes a scheme that is identical to one defined by Apple, the Apple-provided app is launched instead of your app.

To communicate with an app using a custom URL, create an NSURL object with some properly formatted content and pass that object to the openURL: method of the shared UIApplication object. The openURL: method launches the app that registered to receive URLs of that type and passes it the URL. At that point, control passes to the new app.

The following code fragment illustrates how one app can request the services of another app (“todolist” in this example is a hypothetical custom scheme registered by an app):

NSURL *myURL = [NSURL URLWithString:@"todolist://www.acme.com?Quarterly%20Report#200806231300"];
[[UIApplication sharedApplication] openURL:myURL];

If your app defines a custom URL scheme, it should implement a handler for that scheme as described in “Implementing Custom URL Schemes.” For more information about the system-supported URL schemes, including information about how to format the URLs, see Apple URL Scheme Reference.

Supporting AirDrop

In iOS 7, AirDrop lets you share photos, documents, URLs, and other types of data with nearby devices. AirDrop takes advantage of peer-to-peer networking to find nearby devices and connect to them. Apps that include support for sending or receiving files and data automatically gain support for AirDrop.

Sending Files and Data to Another App

To send files and data using AirDrop, display an activity sheet from your user interface using the UIActivityViewController class. When creating this view controller, you must specify the data objects that you want to share. Different activities support different types of data. For AirDrop, you can specify images, strings, URLs, and several other types of data. One way to vend that data is to have your data objects adopt the UIActivityItemSource protocol, which makes it easier to manage different types of data and provide them to the activity view controller.

To display an activity view controller, you can use code similar to that shown in Listing 6-1. The activity view controller automatically uses the type of the specified object to determine what activities to display in the activity sheet. You do not have to specify the AirDrop activity explicitly. However, you can prevent the sheet from displaying specific types using the view controller’s excludedActivityTypes property. When displaying an activity view controller on iPad, you must use a popover.

Listing 6-1  Displaying an activity sheet on iPhone

- (void)displayActivityControllerWithDataObject:(id)obj {
   UIActivityViewController* vc = [[UIActivityViewController alloc]
                                initWithActivityItems:@[obj] applicationActivities:nil];
    [self presentViewController:vc animated:YES completion:nil];
}

For more information about using the activity view controller, see UIActivityViewController Class Reference. For a complete list of activities and the data types they support, see UIActivity Class Reference.

Receiving Files and Data Sent to Your App

To receive files sent to your app using AirDrop, you must do the following:

  • In Xcode, declare support for the document types your app is capable of opening.

  • In your app delegate, implement the application:openURL:sourceApplication:annotation: method to be notified when new data arrives.

  • Be prepared to look for files in your app’s Documents/Inbox directory and move them out of that directory as needed.

The Info tab of your Xcode project contains a Document Types section for specifying the document types your app supports. At a minimum, you must specify a name for your document type and one or more UTIs that represent the data type. For example, to declare support for PNG files, you would include public.png as the UTI string. iOS uses the types you specify to determine if your app is eligible to open a given document.

After transferring an eligible document to your app’s home directory, iOS launches your app (if needed) and calls your app’s application:openURL:sourceApplication:annotation: method. This method is your app’s notification that the file is available for you to examine. Whether you do anything with the file immediately depends on your app and whether it is in the foreground or background. If your app is in the background, you might decide only to note that the file is there so that you can open it later. Because dropped data files are encrypted using data protection, the file would not be available for you to open right away anyway if the device is currently locked.

Documents transferred to your app using AirDrop are placed in the Documents/Inbox directory of your app’s home directory. Apps have permission to read and delete files in this directory but they do not have permission to change the contents of files. It is recommended that you move files out of this directory or delete them when you no longer need them. If you plan to modify the file, you must move it out of the Inbox directory before doing so.

For more information about supporting document types in your app, see Document-Based App Programming Guide for iOS.

Implementing Custom URL Schemes

If your app can receive specially formatted URLs, you should register the corresponding URL schemes with the system. A custom URL scheme is a mechanism through which third-party apps can communicate with each other. Apps often use custom URL schemes to vend services to other apps. For example, the Maps app supports URLs for displaying specific map locations.

Registering Custom URL Schemes

To register a URL type for your app, include the CFBundleURLTypes key in your app’s Info.plist file. The CFBundleURLTypes key contains an array of dictionaries, each of which defines a URL scheme the app supports. Table 6-2 describes the keys and values to include in each dictionary.

Table 6-2  Keys and values of the CFBundleURLTypes property

Key

Value

CFBundleURLName

A string containing the abstract name of the URL scheme. To ensure uniqueness, it is recommended that you specify a reverse-DNS style of identifier, for example, com.acme.myscheme.

The string you specify is also used as a key in your app’s InfoPlist.strings file. The value of the key is the human-readable scheme name.

CFBundleURLSchemes

An array of strings containing the URL scheme names—for example, http, mailto, tel, and sms.

Handling URL Requests

An app that has its own custom URL scheme must be able to handle URLs passed to it. All URLs are passed to your app delegate, either at launch time or while your app is running or in the background. To handle incoming URLs, your delegate should implement the following methods:

If your app is not running when a URL request arrives, it is launched and moved to the foreground so that it can open the URL. The implementation of your application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method should retrieve the URL from its options dictionary and determine whether the app can open it. If it can, return YES and let your application:openURL:sourceApplication:annotation: (or application:handleOpenURL:) method handle the actual opening of the URL. (If you implement both methods, both must return YES before the URL can be opened.) Figure 6-1 shows the modified launch sequence for an app that is asked to open a URL.

Figure 6-1  Launching an app to open a URL

If your app is running but is in the background or suspended when a URL request arrives, it is moved to the foreground to open the URL. Shortly thereafter, the system calls the delegate’s application:openURL:sourceApplication:annotation: to check the URL and open it. If your delegate does not implement this method (or the current system version is iOS 4.1 or earlier), the system calls your delegate’s application:handleOpenURL: method instead. Figure 6-2 shows the modified process for moving an app to the foreground to open a URL.

Figure 6-2  Waking a background app to open a URL

All URLs are passed to your app in an NSURL object. It is up to you to define the format of the URL, but the NSURL class conforms to the RFC 1808 specification and therefore supports most URL formatting conventions. Specifically, the class includes methods that return the various parts of a URL as defined by RFC 1808, including the user, password, query, fragment, and parameter strings. The “protocol” for your custom scheme can use these URL parts for conveying various kinds of information.

In the implementation of application:openURL:sourceApplication:annotation: shown in Listing 6-2, the passed-in URL object conveys app-specific information in its query and fragment parts. The delegate extracts this information—in this case, the name of a to-do task and the date the task is due—and with it creates a model object of the app. This example assumes that the user is using a Gregorian calendar. If your app supports non-Gregorian calendars, you need to design your URL scheme accordingly and be prepared to handle those other calendar types in your code.

Listing 6-2  Handling a URL request based on a custom scheme

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
        sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    if ([[url scheme] isEqualToString:@"todolist"]) {
        ToDoItem *item = [[ToDoItem alloc] init];
        NSString *taskName = [url query];
        if (!taskName || ![self isValidTaskString:taskName]) { // must have a task name
            return NO;
        }
        taskName = [taskName stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 
        item.toDoTask = taskName;
        NSString *dateString = [url fragment];
        if (!dateString || [dateString isEqualToString:@"today"]) {
            item.dateDue = [NSDate date];
        } else {
            if (![self isValidDateString:dateString]) {
                return NO;
            }
            // format: yyyymmddhhmm (24-hour clock)
            NSString *curStr = [dateString substringWithRange:NSMakeRange(0, 4)];
            NSInteger yeardigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(4, 2)];
            NSInteger monthdigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(6, 2)];
            NSInteger daydigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(8, 2)];
            NSInteger hourdigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(10, 2)];
            NSInteger minutedigit = [curStr integerValue];
 
            NSDateComponents *dateComps = [[NSDateComponents alloc] init];
            [dateComps setYear:yeardigit];
            [dateComps setMonth:monthdigit];
            [dateComps setDay:daydigit];
            [dateComps setHour:hourdigit];
            [dateComps setMinute:minutedigit];
            NSCalendar *calendar = [s[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
            NSDate *itemDate = [calendar dateFromComponents:dateComps];
            if (!itemDate) {
                return NO;
            }
            item.dateDue = itemDate;
        }
 
        [(NSMutableArray *)self.list addObject:item];
        return YES;
    }
    return NO;
}

Be sure to validate the input you get from URLs passed to your app; see “Validating Input and Interprocess Communication” in Secure Coding Guide to find out how to avoid problems related to URL handling. To learn about URL schemes defined by Apple, see Apple URL Scheme Reference.

Showing and Hiding the Keyboard

The appearance of the keyboard is tied to the responder status of views. If a view is able to become the first responder, the system shows the keyboard whenever that view actually becomes the first responder. When the user taps another view that does not support becoming the first responder, the system hides the keyboard if it is currently visible. In UIKit, only views that support text entry can become the first responder by default. Other views must override the canBecomeFirstResponder method and return YES if they want the keyboard to be shown.

When a view becomes the first responder, the keyboard is shown by default, but you can replace the keyboard for views that support custom forms of input. Every responder object has an inputView property that contains the view to be displayed when the responder becomes the first responder. When this property is nil, the system displays the standard keyboard. When this property is not nil, the system displays the view you provide instead.

Normally, user taps dictate which view becomes the first responder in your app, but you can force a view to become the first responder too. Calling the becomeFirstResponder method any responder object causes that object to try to become the first responder. If that responder object is able to become the first responder, the custom input view (or the standard keyboard) is shown automatically.

For more information about using the keyboard, see Text Programming Guide for iOS.

Turning Off Screen Locking

If an iOS-based device does not receive touch events for a specified period of time, the system turns off the screen and disables the touch sensor. Locking the screen is an important way to save power. As a result, you should generally leave this feature enabled. However, for an app that does not rely on touch events, such as a game that uses the accelerometers for input, disable screen locking to prevent the screen from going dark while the app is running. However, even in this case, disable screen locking only while the user is actively engaged with the app. For example, if the user pauses a game, reenable screen locking to allow the screen to turn off.

To disable screen locking, set the idleTimerDisabled property of the shared UIApplication object to YES. Be sure to reset this property to NO when your app does not need to prevent screen locking.