iPhone OS Reference Library Apple Developer Connection spyglass button

Files and Networking

An application running in iPhone OS has access to the local file system and network, which you can access using assorted Core OS and Core Services frameworks. Being able to read and write files in the local file system lets you save user data and application state for subsequent uses. Accessing the network gives you the ability to communicate with network servers to perform remote operations and send and retrieve data.

File and Data Management

Files in iPhone OS share space with the user’s media and personal files on the flash-based memory. For security purposes, your application is placed in its own directory and is limited to reading and writing files in that directory only. The following sections describe the structure of an application’s local file system and several techniques for reading and writing files.

Commonly Used Directories

For security purposes, an application has only a few locations in which it can write its data and preferences. When an application is installed on a device, a home directory is created for the application. Table 6-1 lists some of the important subdirectories inside the home directory that you might need to access. This table describes the intended usage and access restrictions for each directory and whether the directory’s contents are backed up by iTunes. For more information about the backup and restore process, see “Backup and Restore.” For more information about the application home directory itself, see “The Application Sandbox.”

Table 6-1  Directories of an iPhone application

Directory

Description

<Application_Home>/AppName.app

This is the bundle directory containing the application itself. Because an application must be signed, you must not make changes to the contents of this directory at runtime. Doing so may prevent your application from launching later.

In iPhone OS 2.1 and later, the contents of this directory are not backed up by iTunes. However, iTunes does perform an initial sync of any applications purchased from the App Store.

<Application_Home>/Documents/

This is the directory you should use to write any application-specific data files. Use this directory to store user data or other information that should be backed up regularly. For information about how to get the path of this directory, see “Getting Paths to Application Directories.”

The contents of this directory are backed up by iTunes.

<Application_Home>/Library/Preferences

This directory contains application-specific preference files. You should not create preferences files directly but should instead use the NSUserDefaults class or CFPreferences API to get and set application preferences; see “Adding the Settings Bundle.”

The contents of this directory are backed up by iTunes.

<Application_Home>/Library/Caches

Use this directory to write any application-specific support files that you want to persist between launches of the application. Your application is generally responsible for adding and removing these files. However, iTunes removes these files during a full restore of the device so you should be able to recreate them as needed. To access this directory, use the interfaces described in “Getting Paths to Application Directories” to get the path to the directory.

In iPhone OS 2.2 and later, the contents of this directory are not backed up by iTunes.

<Application_Home>/tmp/

Use this directory to write temporary files that you do not need to persist between launches of your application. Your application should remove files from this directory when it determines they are no longer needed. (The system may also purge lingering files from this directory when your application is not running.) For information about how to get the path of this directory, see “Getting Paths to Application Directories.”

In iPhone OS 2.1 and later, the contents of this directory are not backed up by iTunes.

Backup and Restore

You do not have to prepare your application in any way for backup and restore operations. In iPhone OS 2.2 and later, when a device is plugged into a computer and synced, iTunes performs an incremental backup of all files, except for those in the following directories:

Although iTunes does back up the application bundle itself, it does not do this during every sync operation. Applications purchased from the App Store on the device are backed up when that device is next synced with iTunes. Applications are not backed up during subsequent sync operations though unless the application bundle itself has changed (because the application was updated, for example).

To prevent the syncing process from taking a long time, you should be selective about where you place files inside your application’s home directory. The <Application_Home>/Documents directory should be used to store user data files or files that cannot be easily recreated by your application. Files used to store temporary data should be placed inside the Application Home/tmp directory and deleted by your application when they are no longer needed. If your application creates data files that can be used during subsequent launches, it should place those files in the Application Home/Library/Caches directory.

Note: If your application creates large data files, or files that change frequently, you should consider storing them in the Application Home/Library/Caches directory and not in the <Application_Home>/Documents directory. Backing up large data files can slow down the backup process significantly. The same is true for files that change regularly (and therefore must be backed up frequently). Placing these files in the Caches directory prevents them from being backed up (in iPhone OS 2.2 and later) during every sync operation.

For additional guidance about how you should use the directories in your application, see Table 6-1.

Files Saved During Application Updates

Updating an application replaces the previous application with the new one downloaded by the user. During this process, iTunes installs the updated application in a new application directory. It then moves the user’s data files from the old installation over to the new application directory before deleting the old installation. Files in the following directories are guaranteed to be preserved during the update process:

Although files in other user directories may also be moved over, you should not rely on them being present after an update.

Keychain Data

A keychain is a secure, encrypted container for passwords and other secrets. The keychain data for an application is stored outside of the application sandbox. If an application is uninstalled, that data is automatically removed. When the user backs up application data using iTunes, the keychain data is also backed up. However, it can only be restored to the device from which the backup was made. An upgrade of an application does not affect its keychain data.

For more on the iPhone OS keychain, see “Keychain Services Concepts” in Keychain Services Programming Guide.

Getting Paths to Application Directories

At various levels of the system, there are programmatic ways to obtain file-system paths to the directory locations of an application’s sandbox. However, the preferred way to retrieve these paths is with the Cocoa programming interfaces. The NSHomeDirectory function (in the Foundation framework) returns the path to the top-level home directory—that is, the directory containing the application, Documents, Library, and tmp directories. In addition to that function, you can also use the NSSearchPathForDirectoriesInDomains and NSTemporaryDirectory functions to get exact paths to your Documents, Caches, and tmp directories.

Both the NSHomeDirectory and NSTemporaryDirectory functions return the properly formatted path information in an NSString object. You can use the path-related methods of NSString to modify the path information or create new path strings. For example, upon retrieving the temporary directory path, you could append a file name and use the resulting string to create a file with the given name in the temporary directory.

Note: If you are using frameworks with ANSI C programmatic interfaces—including those that take paths—recall that NSString objects are “toll-free bridged” with their Core Foundation counterparts. This means that you can cast a NSString object (such as the return by one of the above functions) to a CFStringRef type, as shown in the following example:

CFStringRef homeDir = (CFStringRef)NSHomeDirectory();
For more information on toll-free bridging, see Carbon-Cocoa Integration Guide.

The NSSearchPathForDirectoriesInDomains function of the Foundation framework lets you obtain the full path to several application-related directories. To use this function in iPhone OS, specify an appropriate search path constant for the first parameter and NSUserDomainMask for the second parameter. Table 6-2 lists several of the most commonly used constants and the directories they return.

Table 6-2  Commonly used search path constants

Constant

Directory

NSDocumentDirectory

<Application_Home>/Documents

NSCachesDirectory

<Application_Home>/Library/Caches

NSApplicationSupportDirectory

<Application_Home>/Library/Application Support

Because the NSSearchPathForDirectoriesInDomains function was designed originally for Mac OS X, where multiple such directories could exist, it returns an array of paths rather than a single path. In iPhone OS, the resulting array should contain the single path to the desired directory. Listing 6-1 shows a typical use of this function.

Listing 6-1  Getting a file-system path to the application’s Documents/ directory

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

You can call NSSearchPathForDirectoriesInDomains using a domain-mask parameter other than NSUserDomainMask or a directory constant other than those in Table 6-2, but the application will be unable to write to any of the returned directory locations. For example, if you specify NSApplicationDirectory as the directory parameter and NSSystemDomainMask as the domain-mask parameter, you get the path /Applications returned (on the device), but your application cannot write any files to this location.

Another consideration to keep in mind is the difference in directory locations between platforms. The paths returned by NSSearchPathForDirectoriesInDomains, NSHomeDirectory, NSTemporaryDirectory, and similar functions differ depending on whether you’re running your application on the device or on the Simulator. For example, take the function call shown in Listing 6-1. On the device, the returned path (documentsDirectory) is similar to the following:

/var/mobile/Applications/30B51836-D2DD-43AA-BCB4-9D4DADFED6A2/Documents

However, on the Simulator, the returned path takes the following form:

/Volumes/Stuff/Users/johnDoe/Library/Application Support/iPhone Simulator/User/Applications/118086A0-FAAF-4CD4-9A0F-CD5E8D287270/Documents

To read and write user preferences, use the NSUserDefaults class or the CFPreferences API. These interfaces eliminate the need for you to construct a path to the Library/Preferences/ directory and read and write preference files directly. For more information on using these interfaces, see “Adding the Settings Bundle.”

If your application contains sound, image, or other resources in the application bundle, you should use the NSBundle class or CFBundleRef opaque type to load those resources. Bundles have an inherent knowledge of where resources live inside the application. In addition, bundles are aware of the user’s language preferences and are able to choose localized resources over default resources automatically. For more information on bundles, see “The Application Bundle.”

Reading and Writing File Data

The iPhone OS provides several ways to read, write, and manage files.

Note: The preceding list of Core OS calls is just a sample of the more commonly used calls. For a more complete list of the available functions, see the list of functions in section 3 of iPhone OS Manual Pages.

The following sections show examples of how to use some of the higher-level techniques for reading and writing files. For additional information about the file-related classes of the Foundation framework, see Foundation Framework Reference.

Reading and Writing Property List Data

A property list is a form of data representation that encapsulates several Foundation (and Core Foundation) data types, including dictionaries, arrays, strings, dates, binary data, and numerical and Boolean values. Property lists are commonly used to store structured configuration data. For example, the Info.plist file found in every Cocoa and iPhone applications is a property list that stores configuration information about the application itself. You can use property lists yourself to store additional information, such as the state of your application when it quits.

In code, you typically construct a property list starting with either a dictionary or array as a container object. You then add other property-list objects, including (possibly) other dictionaries and arrays. The keys of dictionaries must be string objects. The values for those keys are instances of NSDictionary, NSArray, NSString, NSDate, NSData, and NSNumber.

For an application whose data can be represented by a property-list object (such as an NSDictionary object), you could write that property list to disk using a method such as the one shown in Listing 6-2. This method serializes the property list object into an NSData object, then calls the writeApplicationData:toFile: method (the implementation for which is shown in Listing 6-4) to write that data to disk.

Listing 6-2  Converting a property-list object to an NSData object and writing it to storage

- (BOOL)writeApplicationPlist:(id)plist toFile:(NSString *)fileName {
    NSString *error;
    NSData *pData = [NSPropertyListSerialization dataFromPropertyList:plist format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
    if (!pData) {
        NSLog(@"%@", error);
        return NO;
    }
    return ([self writeApplicationData:pData toFile:(NSString *)fileName]);
}

When writing property list files in iPhone OS, it is important to store your files in binary format. You do this by specifying the NSPropertyListBinaryFormat_v1_0 key in the format parameter of the dataFromPropertyList:format:errorDescription: method. The binary property-list format is much more compact than the other format options, which are text based. This compactness not only minimizes the amount of space taken up on the user’s device, it also improves the time it takes to read and write the property list.

Listing 6-3 shows the corresponding code for loading a property-list file from disk and reconstituting the objects in that property list.

Listing 6-3  Reading a property-list object from the application’s Documents directory

- (id)applicationPlistFromFile:(NSString *)fileName {
    NSData *retData;
    NSString *error;
    id retPlist;
    NSPropertyListFormat format;
 
    retData = [self applicationDataFromFile:fileName];
    if (!retData) {
        NSLog(@"Data file not returned.");
        return nil;
    }
    retPlist = [NSPropertyListSerialization propertyListFromData:retData  mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
    if (!retPlist){
        NSLog(@"Plist not returned, error: %@", error);
    }
    return retPlist;
}

For more on property lists and the NSPropertyListSerialization class, see Property List Programming Guide.

Using Archivers to Read and Write Data

An archiver converts an arbitrary collection of objects into a stream of bytes. Although this may sound similar to the process employed by the NSPropertyListSerialization class, there is an important difference. A property-list serializer can convert only a limited set of (mostly scalar) data types. Archivers can convert arbitrary Objective-C objects, scalar types, arrays, structures, strings, and more.

The key to the archiving process is in the target objects themselves. The objects manipulated by an archiver must conform to the NSCoding protocol, which defines the interface for reading and writing the object’s state. When an archiver encodes a set of objects, it sends an encodeWithCoder: message to each one, which the object then uses to write out its critical state information to the corresponding archive. The unarchiving process reverses the flow of information. During unarchiving, each object receives an initWithCoder: message, which it uses to initialize itself with the state information currently in the archive. Upon completion of the unarchiving process, the stream of bytes is reconstituted into a new set of objects that have the same state as the ones written to the archive previously.

The Foundation framework supports two kinds of archivers—sequential and keyed. Keyed archivers are more flexible and are recommended for use in your application. The following example shows how to archive a graph of objects using a keyed archiver. The representation method of the _myDataSource object returns a single object (possibly an array or dictionary) that points to all of the objects to be included in the archive. The data object is then written to a file whose path is specified by the myFilePath variable.

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[_myDataSource representation]];
[data writeToFile:myFilePath atomically:YES];
Note: You could also send a archiveRootObject:toFile: message to the NSKeyedArchiver object to create the archive and write it to storage in one step.

To load the contents of an archive from disk, you simply reverse the process. After loading the data from disk, you use the NSKeyedUnarchiver class and its unarchiveObjectWithData: class method to get back the model-object graph. For example, to unarchive the data from the previous example, you could use the following code:

NSData* data = [NSData dataWithContentsOfFile:myFilePath];
id rootObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];

For more information on how to use archivers and how to make your objects support the NSCoding protocol, see Archives and Serializations Programming Guide for Cocoa.

Writing Data to Your Documents Directory

After you have an NSData object encapsulating the application data (either as an archive or a serialized property list), you can call the method shown in Listing 6-4 to write that data to the application Documents directory.

Listing 6-4  Writing data to the application’s Documents directory

- (BOOL)writeApplicationData:(NSData *)data toFile:(NSString *)fileName {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    if (!documentsDirectory) {
        NSLog(@"Documents directory not found!");
        return NO;
    }
    NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
    return ([data writeToFile:appFile atomically:YES]);
}

Reading Data from the Documents Directory

To read a file from your application’s Documents directory, construct the path for the file name and use the desired method to read the file contents into memory. For relatively small files—that is, files less than a few memory pages in size—you could use the code in Listing 6-5 to obtain a data object for the file contents. This example constructs a full path to the file in the Documents directory, creates a data object from it, and then returns that object.

Listing 6-5  Reading data from the application’s Documents directory

- (NSData *)applicationDataFromFile:(NSString *)fileName {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
    NSData *myData = [[[NSData alloc] initWithContentsOfFile:appFile] autorelease];
    return myData;
}

For files that would require multiple memory pages to hold in memory, you should avoid loading the entire file all at once. This is especially important if you plan to use only part of the file. For larger files, you should consider mapping the file into memory using either the mmap function or the initWithContentsOfMappedFile: method of NSData.

Choosing when to map files versus load them directly is up to you. It is relatively safe to load a file entirely into memory if it requires only a few (3-4) memory pages. If your file requires several dozen or a hundred pages, however, you would probably find it more efficient to map the file into memory. As with any such determination, though, you should measure your application’s performance and determine how long it takes to load the file and allocate the necessary memory.

File Access Guidelines

When creating files or writing out file data, keep the following guidelines in mind:

Saving State Information

When the user presses the Home button, iPhone OS quits your application and returns to the Home screen. Similarly, if your application opens a URI whose scheme is handled by a different application, iPhone OS quits your application and opens the URI in the other application. In other words, any action that would cause your application to suspend or go to the background in Mac OS X causes your application to quit in iPhone OS. Because these actions happen regularly on mobile devices, your application must change the way it manages volatile data and application state.

Unlike most desktop applications, where the user manually chooses when to save files to disk, your application should save changes automatically at key points in your workflow. Exactly when you save data is up to you, but there are two potential options. Either you can save each change immediately as the user makes it, or you can batch changes on the same page together and save them when the page is dismissed, a new page is displayed, or the application quits. Under no circumstances should you let the user navigate to a new page of content without saving the data on the previous page.

When your application is asked to quit, you should save the current state of your application to a temporary cache file or to the preferences database. The next time the user launches your application, use that information to restore your application to its previous state. The state information you save should be as minimal as possible but still let you accurately restore your application to an appropriate point. You do not have to display the exact same screen the user was manipulating previously if doing so would not make sense. For example, if a user edits a contact and then leaves the Phone application, upon returning, the Phone application displays the top-level list of contacts, rather than the editing screen for the contact.

Case Sensitivity

The file system for iPhone OS–based devices is case sensitive. Whenever you work with filenames, you should be sure that the case matches exactly or your code may be unable to open or access the file.

Networking

The networking stack in iPhone OS includes several interfaces over the radio hardware of iPhone and iPod touch devices. The main programming interface is the CFNetwork framework, which builds on top of BSD sockets and opaque types in the Core Foundation framework to communicate with network entities. You can also use the NSStream classes in the Foundation framework and the low-level BSD sockets found in the Core OS layer of the system.

The following sections provide iPhone-specific tips for developers who need to incorporate networking features into their applications. For information about how to use the CFNetwork framework for network communication, see CFNetwork Programming Guide and CFNetwork Framework Reference. For information about using the NSStream class, see Foundation Framework Reference.

Tips for Efficient Networking

When implementing code to receive or transmit across the network, remember that doing so is one of the most power-intensive operations on a device. Minimizing the amount of time spent transmitting or receiving helps improve battery life. To that end, you should consider the following tips when writing your network-related code:

The cellular and Wi-Fi radios are designed to power down when there is no activity. Doing so can take several seconds though, depending on the radio. If your application transmits small bursts of data every few seconds, the radios may stay powered up and continue to consume power, even when they are not actually doing anything. Rather than transmit small amounts of data more often, it is better to transmit a larger amount of data once or at relatively large intervals.

When communicating over the network, it is also important to remember that packets can be lost at any time. When writing your networking code, you should be sure to make it as robust as possible when it comes to failure handling. It is perfectly reasonable to implement handlers that respond to changes in network conditions, but do not be surprised if those handlers are not called consistently. For example, the Bonjour networking callbacks may not always be called immediately in response to the disappearance of a network service. The Bonjour system service does immediately invoke browsing callbacks when it receives a notification that a service is going away, but network services can disappear without notification. This might occur if the device providing the network service unexpectedly loses network connectivity or the notification is lost in transit.

Using Wi-Fi

If your application accesses the network using the Wi-Fi radios, you must notify the system of that fact by including the UIRequiresPersistentWiFi key in the application’s Info.plist file. The inclusion of this key lets the system know that it should display the network selection panel if it detects any active Wi-Fi hot spots. It also lets the system know that it should not attempt to shut down the Wi-Fi hardware while your application is running.

To prevent the Wi-Fi hardware from using too much power, iPhone OS has a built-in timer that turns off the hardware completely after 30 minutes if no application has requested its use through the UIRequiresPersistentWiFi key. If the user launches an application that includes the key, iPhone OS effectively disables the timer for the duration of the application’s life cycle. As soon as that application quits, however, the system reenables the timer.

Note: Note that even when UIRequiresPersistentWiFi has a value of true, it has no effect when the device is idle (that is, screen-locked). The application is considered inactive and, although it may function on some levels, it has no Wi-Fi connection.

For more information on the UIRequiresPersistentWiFi key and the keys of the Info.plist file, see “The Information Property List.”

The Airplane Mode Alert

If the device is in airplane mode when your application launches, the system may display a panel to notify the user of that fact. The system displays this panel only when all of the following conditions are met:



Last updated: 2009-10-19

Did this document help you? Yes It's good, but... Not helpful...