Thursday, 2 February 2012

Property List Tutorial – using plist to store user data

This tutorial is the first I’m going to do in a series of tutorials dealing with data in our apps. The series will include different ways to bring data into our app as well as ways to save data from our app to use the next time the app is launched. I’m going to start with property lists or plists as they are often called.
Property lists are a convenient way to access and store small amounts of data. You cannot store custom data objects in a plist, only standard types such as String, Integer etc … as well as collections of standard types. Plists most commonly stored in XML format though they can also be stored in binary form. Property lists are very user friendly because there are methods already in place to store an object or array of objects as a plist xml document and to then other methods to read a plist xml document and convert it back into an object or dictionary etc … What this means is you don’t have to do the parsing of the xml yourself.
The App

We’ll be talking more about this in future tutorials, for now we are just going to create a very simple property list as an xml document, save some data to it, and then read that data back out of the plist the next time we run the app.
For more information on plists refer to Apple’s documentation.
1.) Create a new View-based Application named PlistTutorial.
2.) Right click on Resources and select Add > New File, under Mac OS X select Other and then Empty File and name it Data.plist.
Create Empty File
3.) Double click on Data.plist in Xcode to open it up there and add a String “Name”, and an Array “Phones” which has three items. You can add the items by clicking on the little symbol
Tweet Header File
on the far right edge of the line that says root.
Add to the plist
Edit the fields by clicking in them and then typing.
Change the plist element value
After adding the Name String select Root again to display the add icon and repeat the process to add the Array.
add another variable element to the plist
set the data type of the element
Then click the little arrow next to Phone and it will display the add icon so you can add items to the array.
add item to the array
It should look something like this when you are done. You can change the order of the elements just by dragging them.
finished plist
4.) Open up PropertyListExampleViewController.h and add six variables to it and two IBActions. It should be fairly obvious what these are so I won’t go into them in detail. The textFieldReturn method will be used to dismiss the keyboard when the return key is pressed, saveData will get called from a UIButton in the user interface.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import "UIKit/UIKit.h"
 
@interface PlistTutorialViewController : UIViewController
{
 
    IBOutlet UITextField    *nameEntered;
    IBOutlet UITextField    *homePhone;
    IBOutlet UITextField    *workPhone;
    IBOutlet UITextField    *cellPhone;
 
    NSString        *personName;
    NSMutableArray  *phoneNumbers;
 
}
 
@property (nonatomic, retain)   UITextField     *nameEntered;
@property (nonatomic, retain)   UITextField     *homePhone;
@property (nonatomic, retain)   UITextField     *workPhone;
@property (nonatomic, retain)   UITextField     *cellPhone;
 
@property (nonatomic, retain)   NSString        *personName;
@property (nonatomic, retain)   NSMutableArray  *phoneNumbers;
 
- (IBAction) saveData;
- (IBAction) textFieldReturn:(id)textField;
 
@end
Then open up the implementation file and synthesize those variables.
1
2
3
4
5
6
7
#import "PlistTutorialViewController.h"
 
@implementation PlistTutorialViewController
 
@synthesize personName;
@synthesize phoneNumbers;
@synthesize nameEntered, homePhone, workPhone, cellPhone;
5.) Create the user interface, and link up the user interface elements to appropriate IBOutlets. If you need help with this check out one of the tutorials on building a user interface. You just need to make it look something like this.
UI View
Connections Inspector
You need to link each of the UITextField’s to the IBAction textFieldReturn as well and select Did End On Exit. This will allow us to dismiss the keyboard when we press the return key.
Link the UIText Fields to the IBAction
6.) Open up PropertyListExampleViewController.m. We’re going to retrieve the Data.plist file in the viewDidLoad method, and set the textFields in our user interface to the values retrieved from our Data.plist. I’m going to let the comments describe what is going on.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Data.plist code
    // get paths from root direcory
    NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
    // get documents path
    NSString *documentsPath = [paths objectAtIndex:0];
    // get the path to our Data/plist file
    NSString *plistPath = [documentsPath stringByAppendingPathComponent:@"Data.plist"];
 
    // check to see if Data.plist exists in documents
    if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
    {
        // if not in documents, get property list from main bundle
        plistPath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
    }
 
    // read property list into memory as an NSData object
    NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
    NSString *errorDesc = nil;
    NSPropertyListFormat format;
    // convert static property liost into dictionary object
    NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
    if (!temp)
    {
        NSLog(@"Error reading plist: %@, format: %d", errorDesc, format);
    }
    // assign values
    self.personName = [temp objectForKey:@"Name"];
    self.phoneNumbers = [NSMutableArray arrayWithArray:[temp objectForKey:@"Phones"]];
    // display values
    nameEntered.text = personName;
    homePhone.text = [phoneNumbers objectAtIndex:0];
    workPhone.text = [phoneNumbers objectAtIndex:1];
    cellPhone.text = [phoneNumbers objectAtIndex:2];
}
7.) Save everything, Build and Run and at this point and you can see that the values in Data.plist are being read and populating the UITextFields.
Simulator Shot 1
8.) Now we’ll write the Data.plist file with values taken from our UI. Implement the method saveData in our view controller implementation file and write the values in the UITextFields back to our Data.plist file. Again I’ll let the comments explain what is going on.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- (IBAction) saveData
{
    // get paths from root direcory
    NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
    // get documents path
    NSString *documentsPath = [paths objectAtIndex:0];
    // get the path to our Data/plist file
    NSString *plistPath = [documentsPath stringByAppendingPathComponent:@"Data.plist"];
 
    // set the variables to the values in the text fields
    self.personName = nameEntered.text;
    self.phoneNumbers = [[NSMutableArray alloc] initWithCapacity:3];
    [phoneNumbers addObject:homePhone.text];
    [phoneNumbers addObject:workPhone.text];
    [phoneNumbers addObject:cellPhone.text];
 
    // create dictionary with values in UITextFields
    NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects: personName, phoneNumbers, nil] forKeys:[NSArray arrayWithObjects: @"Name", @"Phones", nil]];
 
    NSString *error = nil;
    // create NSData from dictionary
    NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
 
    // check is plistData exists
    if(plistData)
    {
        // write plistData to our Data.plist file
        [plistData writeToFile:plistPath atomically:YES];
    }
    else
    {
        NSLog(@"Error in saveData: %@", error);
        [error release];
    }
}
9.) Now implement the textFieldReturn IBAction in our view controller implementation method so that we can dismiss the keyboard.
1
2
3
4
5
// we use this to dismiss the keyboard when the return key is pressed
- (IBAction) textFieldReturn:(id)textField
{
    [textField resignFirstResponder];
}
10.) Save everything, Build and Run and test it out. When you enter new data into the text fields and click the save data button, it should get saved to the Data.plist file and then be read in the next time the app is loaded.
Final Simulator Shot

0 comments:

Post a Comment

 

Copyright @ 2013 PakTechClub.