Wednesday, March 9, 2011

Read and write data from Plist file



Note : Plists are used to store static data only once you update the values in the plist then your old values will be lost. Hence plist should be used only to hold static values, but if you want to hold the old values then in that case you need to keep a copy of all the values in the plist in a collection type like mutable array of mutable dictionary and update all of them when you are performing any operations in the plist.

Note 2 : The files stored in the project bundle have read only access and if you want to perform read, write, delete operation then in that case the file needs to be copied to your application document directory.


It has been quite a time since we are doing iPhone Programming, and in my earlier blog post i have shown you a way to store user data, now if you want to store some small amount of data lets say strings or some image URLs then in that case you wont be using core data or SQLITE neither you would be using a Mutable array because once you restart your app all the data stored in that array would be lost.

So the solution for this is the property list file or plist file, their is one property list file that will be present in each and every iPhone project that you have created the location to that file is in the resource folder, this property list file handles your application oriented settings now of course you may have your own property list file and for that all you have to do is


a) Select the resource folder and right click on it and select add new file, once done the images below will guide you for the next steps






You can add new items in the property list by clicking the button that is at the end of the table, internally in a property list every data is maintained in an XML format.


Design Phase: For this sample app i will add some data in the property list file which would be present in the bundle and then try to read it and then in phase 2 i am going to programatically add data in the property list file save it in the documents folder of the document directory and then try to read the newly added data.


Phase 1: Adding data to the property list present in the bundle and try reading it.

Step 1: Select the property list file and click on the button that is present at the end of the table, when you click it a new item would appear you can set the type of this new item (String, array, dictionary etc).



Property list saves every data in a key value pair, so its better if we give a proper name in the key section of the property list, (For this demo i have kept the type as array and the name of my key is handset and i have added different types of handset to my property list here's a view)



Step 2: Now we are done setting the values inside our property list file, i will not be taking any viewController files here, i will write a function called as readData which will help me in reading the data of the plist file from the bundle. Here's a view at the code of the readData method which is written in the appDelegate.m file of my project

-(void)readData
{
//stores the path of the plist file present in the bundle
NSString *bundlePathofPlist = [[NSBundle mainBundle]pathForResource:@"Mobiles" ofType:@"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:bundlePathofPlist];
NSArray *dataFromPlist = [dict valueForKey:@"HandSet"];
for(int i =0;i<[dataFromPlist count];i++)
{
NSLog(@"Mobile handset no %d is %@",i+1,[dataFromPlist objectAtIndex:i]);
}
}



Code Explanation: Since i have stored an array in my plist file i am using NSArray class to read the data from the plist by supplying the proper key value using NSDictionary class, i have used NSDictionary because as i said earlier every data in the plist is stored in the key value pair. Here's the output of the above code



Phase 2: Storing the Plist file from the bundle to the document directory and adding data to it

Step 1: The first step would be to copy the plist file from the bundle to the document directory folder, and then adding data to the plist file that is newly created in the documents folder of the document directory, here's the code to do that

-(void)CreateFileandAddData
{
NSArray *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [documentPath objectAtIndex:0];

//the below variable is an instance of the NSString class and is declared inteh .h file
newPlistFile = [documentFolder stringByAppendingPathComponent:@"NewPlist.plist"];
NSString *bundleFile = [[NSBundle mainBundle]pathForResource:@"Mobiles" ofType:@"plist"];

//copy the file from the bundle to the doc directory
[[NSFileManager defaultManager]copyItemAtPath:bundleFile toPath:newPlistFile error:nil];
NSMutableDictionary *addData = [NSMutableDictionary dictionaryWithContentsOfFile:newPlistFile];
NSArray *newHadsets = [[NSArray alloc]initWithObjects:@"LG",@"Samsung",@"Android",@"Spice",nil];
//adding the new objects to the plist
[addData setObject:newHadsets forKey:@"HandSet"];
//finally saving the changes made to the file
[addData writeToFile:newPlistFile atomically:YES];
}

Step 2: Now if you want to read the data present in the plist file then in that case you can alter a small piece of line that was present in our earlier function called as readData, here's a view to that as well

-(void)readData
{
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:newPlistFile];
NSArray *dataFromPlist = [dict valueForKey:@"HandSet"];
for(int i =0;i<[dataFromPlist count];i++)
{
NSLog(@"Mobile handset no %d is %@",i+1,[dataFromPlist objectAtIndex:i]);
}
}

Step 3: Now when you call both the functions you will get the below output into your console


Note: A better option if you are working with document path  it's better to create a function with the return type as string containing the document directory path and then use that function.


-(NSString*) DocumentDirectoryPath
{
NSArray *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [documentPath objectAtIndex:0];
return documentFolder;
}


You may download the source code of this demo from here. The demo contains a helper file for plist you may modify it as per your needs, in case if you find any issues or errors then in that case you may mail me or mention your query as a comment.

I hope that this post helped you out, our comments and suggesstions are always welcome, until then Happy iCoding and have a great Day.

Using GData To parse an XML file

I was using the GData library today and i found that it has been deprecated by google don't know why and don't know when the new files for GData will be available so for temporary period of time this library will not be working. 


I am learning TBXML now and most probably in future i will post for it and also for  NSXMLParser as well so just give me some time for that.




Really sad to see such a brilliant library down.


In today’s post we will see how to parse an XML file using GData XML parser.

Note: Before beginning with the post it is recommended that you have some basic knowledge about XML, DOM and SAX parsers.

Design Phase: For this sample application we will parse an XML file which is present in the bundle and display its content in the table view, the parser that we will use in this case is GData

Before beginning with this tutorial you have to download the GData library.

Step 1: Open Xcode and select the windows based application template and add UITableViewSubclass file to it with the name myviewController, after doing this their would be two files that would be added into your class group [myviewController.h and myviewController.m].

Step 2: It’s time to add the GData library into your project so for that select the downloaded GData file from the download folder and inside the folder you will find a folder with the name source and then inside the source folder you will find a folder named “XML support”, drag and drop this folder into your application, 


once this is done then we will have to do some settings so that GData library works nicely with our application.

Perform the settings that are given below:

1) Select the project tab from the xcode menu and select the edit project settings option


2)You will see a window in front of you that’s your project window from their select the header search path option (you can type header search path in the search bar) and type the following that is given in the image below






3)Once you are done with header search path the other setting that you have to do is set the other linker flag (you may type other linker flag in the search bar), now once you find the other linker flag just type the following data in the image given below






4) Now its time to test whether you have made the correct settings or not and for that you have to import the GDatatXMLNode header file in the  .h file of the myviewController class and press build your application.

Step 3: Here’s a look at our XML file, which is present in the bundle, now inorder to read this file just follow the steps given below



First in the .h file declare the following objects, I have used comments to describe the objects,
#import <UIKit/UIKit.h>
#import "GDataXMLNode.h"

@interface myviewController : UITableViewController {

 //stores the entire data about the XML document
 GDataXMLDocument *xmlDocument;

 //string variable to hold the location of the XML File
 NSString *xmlFileLocation;

 //after parsing the data will be stored in the array
 NSMutableArray *data_from_xml;

 //stores the error information that occured
 NSError *error;
}
@end

Now into the .m file  select the init method and add this piece of code.

-(id)initWithStyle:(UITableViewStyle)style {
    // Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
    if (self = [super initWithStyle:style]) {
 
 
  xmlFileLocation = [[NSBundle mainBundle]pathForResource:@"Cars" ofType:@"xml"];
 
  NSData *xmlData = [NSData dataWithContentsOfFile:xmlFileLocation];
 
  xmlDocument = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:&error];
 
  NSArray *getData = [[xmlDocument rootElement]elementsForName:@"car"];
 
  data_from_xml = [[NSMutableArray alloc]init];
 
  for(GDataXMLElement *e in getData)
  {
   [data_from_xml addObject:e];
  }
    }
    return self;
}

Step 4: Now once the above step is completed then just add the number of sections and rows for section in the table view datasource method of the table view, here’s a view of doing that

 
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [data_from_xml count];
}

Now its time to see some real GData
In the cell for row at index path method when you give title to each cells then in this case the titles will be coming from the XML file so for that what you have to do is select the appropriate elements from the XML file and display them in the table view and here’s how you do that

 
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }
    
    // Set up the cell...
 cell.textLabel.text = [[[[data_from_xml objectAtIndex:indexPath.row]elementsForName:@"company"]objectAtIndex:0]stringValue];
 cell.detailTextLabel.text = [[[[data_from_xml objectAtIndex:indexPath.row]elementsForName:@"model"]objectAtIndex:0]stringValue];
    
 return cell;
}

Step 5: Select the app delegate.m file and add the table view to the window here’s how you will do that

#import "GDataAppAppDelegate.h"
#import "myviewController.h"

@implementation GDataAppAppDelegate

@synthesize window;


- (void)applicationDidFinishLaunching:(UIApplication *)application {    

    // Override point for customization after application launch
 myviewController *obj = [[myviewController alloc]initWithStyle:UITableViewStyleGrouped];
 [window addSubview:obj.view];
    [window makeKeyAndVisible];
}

Click build and go you will see the output as given below




Now lets say the structure of your XML file is somewhat like this




Then in this case what you have to do is use a nested for loop in order to get the data from each and every car element present in the XML file here’s a look at the code

NSArray *getData = [[xmlDocument rootElement]elementsForName:@"FavouriteCar"];
tempArray = [[NSMutableArray alloc]init];
for(GDataXMLElement *e in getData)
{
for(int i =0;i<[[e elementsForName:@"car"]count];i++)
{

//fetching the car model
testString = [[[[e elementsForName:@"car"]objectAtIndex:i]childAtIndex:1]stringValue];
//storing the car model in the mutable array
[tempArray addObject:testString];

}
}

Code Explanation: The above code is self explanatory, I am storing the car model from the xml element in the NSString variable and then in the next line I am adding those values to the instance of a mutable array so that I can show the data in the table.

i hope this post helped you out in parsing an XML file using GData XML parser their are many different parsers available for xml parsing in iPhone you may check them out if you like,

Your comments good or bad are always welcome as they help me in improving my writing skills. 

Until then happy iCoding and have a great Day.