MOBILE

iOS SDK : Property Lists and Archiving - An iOS Application’s Directory Structure, Property Lists

5/15/2013 7:40:20 PM

1. An iOS Application’s Directory Structure

Persisting and archiving require writing data to a file, but an iOS application can only read and write to files in the application’s sandbox. When installed, an application is placed in its own home directory. This directory is the application’s root directory and should be left untouched, lest you risk corrupting your application. Under the application’s home directory are the directories you may write to. These directories are the Documents, Preferences, Caches, and tmp directories.

<application home directory>/Documents
<application home directory>/Library/Preferences
<application home directory>/Library/Caches
<application home directory>/tmp

The Documents directory is where you should write your application’s data files. The Preferences directory is where your application’s preferences are stored. These are the preferences set through the iOS’s Settings application, using the NSUserDefaults class, not preferences you might create programmatically. The Caches directory, like the Documents directory, is another location you can persist files to, although, as the name implies, this directory should be reserved for caching data rather than storing an application’s files. The tmp directory is a temporary directory for writing files that do not need to be persisted between application launches. Your application should remove files from this directory when not needed and iOS also removes files from this folder when an application is not running.

Directories

You will mostly read and write from two directories: the Documents directory and the tmp directory. Files you want to persist between application launches should go in the Documents directory. These files are also backed up by iTunes when an iPhone, iPod touch, or iPad is synchronized with a user’s computer. Files placed in the tmp folder are temporary and should be deleted when an application terminates. If the application does not clean the folder, iOS might delete them depending on when space is needed on your device. You should never hard-code a path in your code to either folder. When using the Documents folder, you should use the NSHomeDirectory method combined with the NSSearchPathForDirectoriesInDomain method. When obtaining the tmp directory, you should use the NSTemporaryDirectory method.

NSHomeDirectory

The NSHomeDirectory is how you should obtain an application’s root directory.

NSString * NSHomeDirectory (void);

Obtain the path to your Documents directory using the NSHomeDirectory. By itself, this method isn’t very useful, as you usually want to obtain the Documents directory.

NSSearchPathForDirectoriesInDomains

Obtain the path to your application’s Documents directory using the NSSearchPathFor DirectoriesInDomains.

NSArray * NSSearchPathForDirectoriesInDomains (
    NSSearchPathDirectory directory,
NSSearchPathDomainMask domainMask, BOOL expandTilde );

The method takes three parameters: the directory to begin the search, the search path domain mask, and a flag indicating if tildes should be converted to actual paths. The method returns an array of paths. Although on a desktop or laptop there might be multiple elements in the array, on an iOS device, there will only be one result in the array. The following code illustrates how to obtain an application’s Documents directory on an iOS device:

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

Values you might use for the directory parameter on an iOS device include NSDocumentDirectory, NSApplicationDirectory, NSCachesDirectory, and NSApplicationSupportDirectory.

NSTemporaryDirectory

The NSTemporaryDirectory method returns the path to your application’s tmp directory.

NSString * NSTemporaryDirectory (void);

Unlike the NSHomeDirectory method, the NSTemporaryDirectory method is useful by itself, as it is the most direct way to obtain a path to your application’s tmp directory.

2. Property Lists

The easiest way to save your application’s preferences if you’re managing them within your application is using a property list. If an object can be serialized, you can persist it to a file using a path or URL. You can also reconstitute the object by reading it from the file.

It is worth noting that only objects can be serialized. A common source of frustration is trying to serialize a primitive int. Since primitive data types are not serializable, they need to be converted to NSObjects (e.g., int to NSNumber).

Simple Serialization

The NSDictionary, NSArray, NSString, NSNumber, and NSData classes, and their mutable equivalents, can all be saved as a property list using the writeToFile: or writeToURL: method.

-(BOOL)writeToFile:(NSString *) path atomically:(BOOL)flag
-(BOOL)writeToURL:(NSURL *) aURL atomically:(BOOL)flag

The first parameter is the path, or URL, to save the file as. The second parameter is a flag indicating if the file should first be saved to an auxiliary file. If the flag is YES, the data is written to an auxiliary file that is then renamed to the file indicated by the path or URL. Writing to an auxiliary file prevents the file system from becoming corrupt should writing the file fail midstream.

Note

You can refer to the NSDictionary, NSArray, NSString, NSNumber, or NSData classes, or one of their mutable equivalents, as a property list object. So you could say “the property list objects all contain . . .” rather than naming each property list object individually.


Reading a property list back into the object uses the initWithContentsOfFile: or initWithContentsOfURL: method.

-(id)initWithContentsOfFile:(NSString *)path
-(id)initWithContentsOfURL:(NSURL *)aURL

The initWithContentsOfFile: method takes a path to the property file, while the initWithContentsOfURL: takes a URL. Both return an id.

Try This: Preserving an NSArray

  1. Create a new View-based Application named SimpleArray.

  2. Open SimpleArrayAppDelegate.m and modify applicationDidFinishLaunching WithOptions to match Listing 1.

  3. Click Run (Listing 2).

  4. After running the application, navigate to properties.plist in the file system and open it using TextEdit (Listing 3).


Listing 1. The applicationDidFinishLaunching method in SimpleArrayAppDelegate.m
(BOOL)application:(UIApplication *)application
          didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSMutableArray * dataArray = [[NSMutableArray alloc]
         initWithObjects: @"First", @"Second", @"Third", nil];
  NSString * path = [(NSString *) [NSSearchPathForDirectoriesInDomains
       (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
       stringByAppendingPathComponent:@"properties.plist"];
  [dataArray writeToFile:path atomically:YES];
  NSArray * dataArray2 = [[NSArray alloc] initWithContentsOfFile:path];
  NSLog(@"objects: %@, %@, %@",
        [dataArray2 objectAtIndex:0], [dataArray2 objectAtIndex:1],
        [dataArray2 objectAtIndex:2]);
  [window addSubview:viewController.view];
  [window makeKeyAndVisible];
  [dataArray release];
  [dataArray2 release];
}

Listing 2. Logging to the Debugger Console
2010-09-11 11:17:41.591 SimpleArray[14500:207] objects: First, Second,
Third

Listing 3. The properties.plist file is saved as XML.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>First</string>
<string>Second</string>
<string>Third</string>
</array>
</plist>

The application first gets a path to its Documents directory and adds the filename to the path. After creating the path, the application persists the array to a file. Immediately after persisting the file, it creates a new array from the file’s content and logs the array’s values to the Debugger Console.

One thing interesting to note is that the application persists the array in an XML format. If you wished, you could easily modify the data in any text editor. You could also persist the application to a URL, and since it is XML with a published document type definition (DTD), you could process the file with almost any back-end programming language that had libraries for parsing XML and DTD files. However, note that the writeToURL:atomically: method is synchronous and your application will halt processing until the data is successfully written to the URL, so you are better off using the NSURLConnection class so that your application doesn’t appear to freeze up until all of the data has been written.

NSPropertyListSerialization

Using the writeToFile: method to save a property list object as a simple property list is usually sufficient, but another way you can persist a property list object to a property list is by using the NSPropertyListSerialization class.

Serializing

To serialize a property list object, use the dataFromPropertyList:format:errorDescription: method.

+(NSData *)dataFromPropertyList:(id)plist format:
(NSPropertyListFormat *)format
errorDescription:(NSString **) errorString

This method’s first parameter is an id that references the property list data and must be a property list object. Note that the dataFromPropertyList:format:errorDescription: method doesn’t open and read a file’s content; you must first obtain the data using the initWithContentsOfFile: or initWithContentsOfURL: method. The method’s second parameter is the property list’s desired format. This parameter is one of the valid NSPropertyListFormat types: NSPropertyListOpenStepFormat, NSPropertyListXMLFormat_ v1_0, or NSPropertyListBinaryFormat_v1_0. The method’s final parameter is a string to place an error description should something fail. Note, you must release this string should an error occur. The method returns an NSData object. You can then write this object to disk, using the writeToFile: or writeToURL: method.

Deserializing

To deserialize a property list, use the propertyListFromData:mutabilityOption:format: errorDescription: method.

+ (id)propertyListFromData:(NSData *)data
     mutabilityOption: (NSPropertyListMutabilityOptions) opt
     format: (NSPropertyListFormat *)format
     errorDescription:(NSString **) errorString

This method’s first parameter is the data to deserialize. The method’s second parameter indicates if the properties should be immutable or mutable. The method’s third parameter indicates the format to make the property list, and the fourth parameter is the error description. Valid values for the second parameter are NSPropertyListImmutable, NSPropertyListMutableContainers, and NSPropertyListMutableContainersAndLeaves. Valid values for the third parameter are NSPropertyListOpenStepFormat, NSPropertyListXMLFormat_v1_0, and NSPropertyListBinary Format_v1_0. Note that as with the dataFromPropertyList: method, should something fail, you must release the NSString holding the error description.

Note

Do not take this task’s more complex data structure as implying you cannot use a property list object’s writeToFile: or writeToURL: method to persist complex data structures. You can, provided all items in a data structure are a property list object. For instance, if an NSArray’s elements each contained an NSDictionary, you could serialize the entire data structure at once by writing the NSArray to a file.


Try This: Preserving to an XML Property List

  1. Create a new View-based Application named Properties.

  2. Open PropertiesAppDelegate.m and modify the applicationDidFinishLaunching WithOptions method (Listing 4).

  3. Click Build And Go.


Listing 4. The PropertiesAppDelegate’s applicationDidFinishLaunchingWithOptions method
- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSString * errorDescription;
  NSString *pathToFile = [[NSSearchPathForDirectoriesInDomains
        (NSDocumentDirectory, NSUserDomainMask,YES) objectAtIndex:0]
        stringByAppendingPathComponent:@"properties.plist"];
  NSData * myData;
  NSLog(@"%@", pathToFile);
  if ([[NSFileManager defaultManager] fileExistsAtPath:pathToFile] == NO) {
    NSMutableDictionary * dict2Serialize =
             [[[NSMutableDictionary alloc] init] autorelease];
    NSString * name = @"James";
    NSArray * kids = [NSArray arrayWithObjects:
                        @"Nicolas", @"Juliana", nil];
     NSNumber * age = [NSNumber numberWithInt:40];
     [dict2Serialize setObject:name forKey:@"name"];
     [dict2Serialize setObject:kids forKey:@"kids"];
     [dict2Serialize setObject:age forKey:@"age"];
     myData = [NSPropertyListSerialization dataFromPropertyList:(id)
        dict2Serialize format:NSPropertyListXMLFormat_v1_0
         errorDescription:&errorDescription];
    if (myData)
        [myData writeToFile:pathToFile atomically:YES];
    else {
         NSLog(@"Error writing to myData, error: %@", errorDescription);
         [errorDescription release];
     }
  }
  else {
    NSLog(@"property file exists....");
    NSPropertyListFormat format;
    NSData * plistData = [NSData dataWithContentsOfFile:pathToFile];
     NSDictionary * props = (NSDictionary *)[NSPropertyListSerialization
               propertyListFromData:plistData
              mutabilityOption:NSPropertyListImmutable
               format: &format errorDescription: &errorDescription];
    if (props) {
        NSLog(@"name: %@", [props objectForKey:@"name"]);
        NSLog(@"age: %i",
           [(NSNumber *)[props objectForKey:@"age"] intValue]);
         NSLog(@"kid: %@", (NSString *)[(NSArray *)
                     [props objectForKey:@"kids"] objectAtIndex:0]);
         NSLog(@"kid: %@", (NSString *)[(NSArray *)
                     [props objectForKey:@"kids"] objectAtIndex:1]);
    } else {
        NSLog(@"Error reading properties, error: %@", errorDescription);
         [errorDescription release];
     }
  }
  [window addSubview:viewController.view];
  [window makeKeyAndVisible];
  return YES;
}


					  

The first time you run the application, the debugger output will contain only a path. The second time, however, the application logs the property list contents to the Debugger Console. Notice that rather than writing the NSDictionary directly to disk, you first transformed it into an NSData object representing the property list. Had this first step of converting to XML gone awry, you would have the error description informing you (hopefully) where the problem occurred. This error handling is not provided using the NSMutableDictionary’s writeToFile: method. After converting to a property list, you then persisted it using the NSData’s writeToFile method. Listing 5 lists the file’s XML content. Upon running the application a second time, you read the property list as an NSData object and converted it to an NSDictionary. To prove that the data was in fact reconstituted correctly, you logged the output to the Debugger Console (Listing 6).

Listing 5. The application’s plist saved as XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www
.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>age</key>
<integer>40</integer>
<key>kids</key>
<array>
<string>Nicolas</string>
<string>Juliana</string>
</array>
<key>name</key>
<string>James</string>
</dict> </plist>

Listing 6. The application’s Debugger Console logging
2010-09-11 11:35:07.918 Properties[14672:207] /Users/bward/Library/
Application Support/iPhone Simulator/4.1/Applications/3D6D7BC3-8957-
4F2A-977B-6016E86F28C4/Documents/properties.plist
2010-09-11 11:35:07.920 Properties[14672:207] property file exists....
2010-09-11 11:35:07.922 Properties[14672:207] name: James
2010-09-11 11:35:07.922 Properties[14672:207] age: 40
2010-09-11 11:35:07.924 Properties[14672:207] kid: Nicolas
2010-09-11 11:35:07.925 Properties[14672:207] kid: Juliana
Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8