iOS | How-to Use Property List Files to Store Data in iPad or iPhone App

One of vital elements of any iOS application is the info.plist file. This graph based file contains important configuration settings for your application to run normally. For instance you set the icons your app requires in the plist file. The same can be said for the storyboard, an entry must be made to tell the application which element to load first.

This property list file is not only used by app but also by the App Store to determine if the application is properly configured to be included in the App Store. For instance, the App Store checks the value of the UIRequiredDeviceCapabilities to see if a user can run the app on a particular device and if that device has the required capabilities for your application.

The plist is a key component of the application bundle. There are several important configurations that may and should be added to the plist file to make the your app function smoothly with other subsystems on the device.

When an application is created, a info.plist is created with a set of keys required to allow your app to run normally and co-exist on the device and be listed in the App Store. Many other other keys can be added according to the requiresments of your app.

Property list files are also used to manage the functionality of an app by providing a useful persistence data store for lightweight storage. Using the provided APIs a developer can easily read and write to property files any data that is needed in differet parts of an application. They can be programmatically created using either the NSMutableArray or the NSMutableDictionary classes where the data can be saved either as XML or in a binary format. Using XML is more flexible and the fact that the data is in a human readable format gives the format a slight advantage over binary. Although binary provides faster data access than XML.

Property List files lose their effectiveness if you try you use them other than their intended use. By this I mean the property files weren't designed for medium to large data storage, for data that doesn't fit into the property list architecture or for objects that require a static mutability.

Using archiving or another lightweight storage mechanism like possibly a SQLite database might be a better choice.

Creating Property List Files

Property list files can be created manually in the Resource sub-directory or programmatically using the NSPropertListSerialization class. Either way once the file is available, you can read and write to the file using Cocoa Touch classes or Core Foundation classes.

A plist file structure consist of a graph of data that is stored in a XML or binary file. To build the graph you would need to nest a NSDictionary or NSArray object within another object of the same type to build the required data structure for your application. The actual data can be stored using either a string, data, a date, a number or a boolean value. Also since all types can be cast automatically with Cocoa types, you can mix and match types from Core Foundation with equivalent types from Cocoa.

Table 1 Abstract types and their representations

Type
XML
Cocoa
Core Foundation
array
array
NSArray (Mutable)
CFArray (CFArrayRef)
dictionary
dict
NSDictionary (Mutable)
CFDictionary (CFDictionaryRef)
string
string
NSString (Mutable)
CFString (CFStringRef)
data
data
NSData
CFData (CFDataRef)
date
date
NSDate
CFDate (CFDateRef)
integer
integer
NSNumber
CFNumber (CFNumberRef)
floating-point number
real
NSNumber
CFNumber (CFNumberRef)
Boolean
true or false
NSNumber (YES or NO)
CFBoolean (CFBooleanRef; kCFBooleanTrue, kCFBooleanFalse)

Creating plist files manually

Manually creating plist is a simple process using the plist Editor in Xcode. To demonstrate I will walk you through the process of creating a file and building a simple data structure using the types from the table above.

  • Create a new project or open an existing one
  • Once the project is created, right click on the Resource sub directory and add a new Property List file from the Resource group in the new file ActionSheet. Name the file app.plist.
  • For the data structure I will use countries and continents. If you right click anywhere in the workspace of the open file, you will be presented with a context menu that contains an entry to add a row.
  • Click on Add Row menu item to start adding values to the file. You will be immediately provided with a new row with an editable text field. Enter "Geography" as the root element with no corresponding value. Change the type to Dictionary.
  • Right click the geography entry to add a new level to the root. If you click on the plus "+" symbol on the previously created row, the editor will add a new row, but it won't be under the root element unless you expand the element first. Name the newly created entry "Continent" changing the type to Dictionary.
  • Now expand the Continent node and click on the "+" plus symbol to add another row as a child of the Continent row. Name this row "Name", leaving the type as string and provide a name for the Name element like "North America". You can provide the name you want.
  • Add another row as a child of the Continent node. Name this element, Country with a string type and add a value of your choice. For this example, I will use "United States". The app.plist data structure should resemble Figure 1.

Figure 1 : Property List Layout
Figure 1 : Property List Layout

Once the app is built, the app.plist file will be added to the Documents directory for the apps container, so you will be able to add new values to the existing nodes, or if you have to, you could add extra nodes at runtime like we will see next.

Creating plist files programmatically

Creating property-list files is essentially the same technique as we have seen in the previous section on creating property-list files manually. You build a graph of data elements using a combination of NSDictionary or NSArray objects that have one the following types: NSNumber, NSData or NSString.

To demonstrate how to create plist with Objective-C, I will reproduce the same data structure as the previous example so it will be easier to follow along since you will have a visual aid.

  • Create a new class as a subclass of NSObject.
  • Open the header file and create the variables and a method as in code listing 1,

Code Listing 1: MyAppPlist.h

#import <Foundation/Foundation.h>

@interface MyAppPlist : NSObject
{
    CFStringRef trees[3];
    CFArrayRef treeArray;
    CFDataRef xmlValues;
    BOOL fileStatus;
    CFURLRef fileURL;
    SInt32 errNbr;
    CFPropertyListRef plist;
    CFStringRef errStr;
}

@property(nonatomic, retain) NSMutableDictionary * rootElement;
@property(nonatomic, retain) NSMutableDictionary * continentElement;
@property(nonatomic, strong) NSString * name;
@property(nonatomic, strong) NSString * country;
@property(nonatomic, strong) NSArray * elementList;
@property(nonatomic, strong)  id plistData;
@property(nonatomic, strong) NSString * plistPath;
@property(nonatomic, strong) NSData * data;
@property(nonatomic, strong) id filePathObj;
          


-(void)CreateAppPlist;

-(void)ReadAppPlist;

-(void)CreateCFPlist;

-(void)ReadCFPlist;

@end
  • In the implementation file add the code as in code listing 2
  • Start by synthesizing the variables.
  • Next set the path for the app2.plist file.
  • Next initialize the rootElement NSMutableDictionary with a initWithCapacity of 1.
  • Create a NSError variable to capturing any errors.
  • Set the name and country variables as the same values from above. Of course you are free to use whichever values that best fits your regional preferences.
  • Add the name and country variables to the elementList NSArray.
  • Set the continentElement NSMutableDictionary by adding the elementList array and keys.
  • Then add the continentElement to the rootElement NSMutableDictionary.
  • Finally write out the data to the plistPath file variable.

Code Listing 2: Implementation of the CreateAppPList Method using Cocoa Touch

#import "MyAppPlist.h"

@implementation MyAppPlist
@synthesize rootElement, continentElement, country, name, elementList, plistData, data, plistPath;

-(void)CreateAppPlist{
    
    //Get path of app2.plist file to be created
    plistPath = [[NSBundle mainBundle] pathForResource:@"app2" ofType:@"plsit"];
    //Create the data structure
    rootElement = [NSMutableDictionary dictionaryWithCapacity:3];
    NSError * err;
    name = @"North America";
    country = @"United States";
    
    continentElement = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:name,country, nil] forKeys:[NSArray arrayWithObjects:@"Name", @"Country", nil ]];
    
    [rootElement setObject:continentElement forKey:@""];
    
    //Create plist file and serialize XML
   
    data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
    if(data)
    {
        [data writeToFile:plistPath atomically:YES];
        
    }else{
        NSLog(@"An error has occured %@", err);
        
    }
                   
}
@end

Reading Property List Files with Cocoa Touch

Reading the contents of a property file can be accomplished using the initWithContentsOfFile or the initWithContentsOfURL or the dictionary classes dictionaryWithContentsOfURL or dictionaryWithContentsOfFile. From this point, it is only a question of extracting the values from the dictionary.

The iOS SDK provides even more control to the contents of a property list file by using the propertyListFromData:mutabilityOption:format:errorDescription. Using this class you can define which part of the data structure that you want to edit. For instance if you use the NSPropertyListImmutable then all objects will be editable. You could choose to block access to the container (dictionary or array) by using the NSPropertyListMutableContainers or to block access to all object by using the NSPropertyListMutableContainersAndLeaves.

To demonstrate how to read from a property list file, I will use the data and sample app.plist file from the previous examples.

Create a new method to read in the property values, readAppPlist.

  • In the implementation, start by initializing the plistPath variable using the pathForResource method from the NSBundle class, passing in the app.plist parameters.
  • Use the plistPath variable to initialize a new NSMutableDictionary object using the initWithContentsOfFile because the plistData variable is a string. To use the initWithContentsOfURL, you would need to use a NSURL variable.
  • Re-assign the value for each object in the dictionary as before. See listing 3.

Code Listing 3 Reading from Property List file using initWithContentsOfFile

-(void)readAppPlist{
    plistPath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"plist"];
    NSMutableDictionary * propertyDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
    name = [propertyDict objectForKey:@"Name"];
    country = [propertyDict objectForKey:@"Country"];
    
    //use the variables in the app
}

Creating and Writing to Property List Files with Core Foundation

The Core Foundation has several classes for reading and writing to Resource files. To create a Property List, I will create a new method called CreateCFPlist in the same sample application that will create the XML Property List data structure and write to a file using the Core Foundation classes. The complet code is provided in listing 4 and 5.

Listing 4 contains the code from the header file. Add the variables to create and later read from the property file.

  • The CFStringRef will be the elements in the array.
  • The CFArrayRef variable, treeArray, is the property list data structure.
  • The fileStatus will indicate if the write is successful. Error checking is very important when using Core Foundation classes since they can crash your app.
  • The CFURLRef variable, fileURL, is the path and file for the property list.
  • Any errors will be caught with the SInt32 errNbr variable.
  • We will also add a method, -(void)createCFPlist, to handle the operations.

Code Listing 4 Declare Variables in Header File

@interface MyAppPlist : NSObject
{
    CFStringRef trees[3];
    CFArrayRef treeArray;
    CFDataRef xmlValues;
    BOOL fileStatus;
    CFURLRef fileURL;
    SInt32 errNbr;
}

-(void)createCFPlist;
  • In the implementation file, start by assigning a value to each of the elements in the array.
  • Then use the CFArrayCreate to create the property list data structure and assign it to the treeArray variable.
  • Next we will check if the assignment was successful and if not handle the error. Otherwise convert the data to a XML using the CFPropertyListCreateXMLdata.
  • If the operation is successful create a file object uisng the id pointer and cast the CFURLref to the NSURL and get a handler on the file.
  • Write the contents to the file. If everything is successful, release the Core Foundation objects. The code for the implementation is listed in listing 5.

Code Listing 5 The CreateCFPlist Method

-(void)CreateCFPlist{

    //add values for XML
    trees[0] = CFSTR("Douglas Fir");
    trees[1] = CFSTR("Giant Sequoia");
    trees[2] = CFSTR("Australian Mountain-Ash");
    
    //create plist
    treeArray = CFArrayCreate(kCFAllocatorDefault, (const void **)trees, 3, &kCFTypeArrayCallBacks);
    
    if(CFArrayGetCount(treeArray)==0)
    {
        // handle error
    }
    //convert array to XML and write to property list format
    xmlValues = CFPropertyListCreateXMLData(kCFAllocatorDefault, treeArray);
    
    if(!CFPropertyListIsValid(xmlValues, kCFPropertyListXMLFormat_v1_0)){
        //handle Property list error
    }else{
    
    //write XML to a file
    filePathObj = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"plist"];
    fileURL = (__bridge CFURLRef)[[NSURL alloc] initFileURLWithPath:filePathObj];
    fileStatus = CFURLWriteDataAndPropertiesToResource(fileURL, xmlValues, NULL, &errNbr);
    }
    if(!fileStatus){
        // handle &errNbr
    }else{
        //Clean up objects -- Remember from chapter 1 I mentioned that CF objects were excluded from ARC
        CFRelease(treeArray);
        CFRelease(xmlValues);

    }
}

Reading Property List Files with Core Foundation

Reading from a Property List file using the Core Foundation is accomplished using the CFURLCreateDataAndPropertiesFromResource which is assigned to a CFData object. If successful, you then convert the CFData to XML using the CFPropertyListCreateFromXMLData method. Once the data is in the graph, you can extract the data from the array or dictionary.

To demonstrate the process, I have added a new method, readCFPlist to the project and added some code to read the content of the XML file. Here is the steps and the code is in listing 6 below.

  • In the header file, as before, add a new method: readCFPlist
  • Also add two variables, one for the property list and the other for any error string.

Code Listing 6 Add Variables and method to Header to Read Data

    CFPropertyListRef plist;
    CFStringRef errStr;
-(void)readCFPlist;
  • In the implementation file, code listing 7, start by assigning the file object to the filePathObj variable to get the path of the file resource.
  • Next we will get a handle on the file using the NSURL class and casting the object to a CFUrlRef using Toll-Free Bridging keyword: _bridge.
  • Then use the CFURLCreateDataAndPropertiesFromResource to actually load the data from the fileUrl object.
  • Reconstitute the dictionary using the CFPropertyListCreateFromXMLData
  • If successful process the data.

Code Listing 7 Implementing the ReadCFPlist method

-(void)ReadCFPlist{
    filePathObj = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"plist"];
    fileURL = (__bridge CFURLRef)[[NSURL alloc] initFileURLWithPath:filePathObj];
    
    fileStatus = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL,
                                                               &xmlValues, NULL, NULL, &errNbr);
    
    plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, xmlValues, kCFPropertyListImmutable, &errStr);
    if(plist)
    {
        //handle the data
        
    }else{
        NSLog(@"%@",errStr);
        CFRelease(xmlValues);
    }
}

You can then use the data to assign it to whatever operation is being processed, possibly displaying the data on screen or passing variables from one View Controller to the next.

In Conclusion

Property List file or plist files are a great way to store small amounts of data. They also can be used to store variable data to be used in different parts of an app instead of global variables

More by this Author


Comments 1 comment

Natalia 2 years ago

 ( 2012.02.21 19:01 ) : I need to demonstrate my own thkans to this article writer exclusively for rescuing me through such a placing. As a result of browsing on from the the net as well as getting guidelines which are not really enjoyable, I thought my well being was more than. Living minus the strategies to the problems you've solved by way of all of your site is a critical scenario, as well as ones that might possess terribly ruined my entire career basically had not identified the web site. Much of your mastery as well as goodness in working with a lot of stuff has been very helpful. I’m uncertain what I could have done easily hadn’t occur upon this kind of stuff like this. I'm also able to at this stage stay up for my potential. Thank you much for the specialized and wonderful aid. We won’t think twice to be able to suggest your internet site to be able to anyone who needs suggestions about this subject material.

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working