plist not giving the data the way entered - ios

Before I start, I am creating plist programmatically
I am storing the path names of images in plist (programmatically). While saving I took NSLog and below is what I have
2013-08-04 15:25:24.044 XXX[12595:13d03] inserting data is 5===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-42-37.25_tstdddd.png
2013-08-04 15:25:24.057 XXX[12595:13d03] inserting data is 4===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-18-20.673_iphone_2.jpg
2013-08-04 15:25:24.086 XXX[12595:13d03] inserting data is 2===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-14-29.292_Spare-Parts-summer-Ad-hyundai.jpg
2013-08-04 15:25:24.087 XXX[12595:13d03] inserting data is 1===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-11-55.395_horizon.jpg
2013-08-04 15:25:24.089 XXX[12595:13d03] inserting data is 6===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-08-03-29.371_2010.jpg
the data is entered with id 5,4,2,1,6.
Now when I read this data, below is what I am getting.
2013-08-04 15:27:28.251 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-18-20.673_iphone_2.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-42-37.25_tstdddd.png
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-11-55.395_horizon.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-08-03-29.371_2010.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-14-29.292_Spare-Parts-summer-Ad-hyundai.jpg
means while fetching I get data with 4,5,1,6,2
When I double click plist, I have data with id sorted i.e. I have data as 1,2,4,5,6.
Any idea why plist is providing random data?
Below is the screen shot of plist that is getting generated.
Edit 1
For Storing data, below is the code I have.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
news = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Offers.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if(![[NSFileManager defaultManager] removeItemAtPath:path error:&error])
{
//TODO: Handle/Log error
NSLog(#"files not deleted...");
} else {
NSLog(#"files deleted...");
}
if (![fileManager fileExistsAtPath: path])
{
path = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat: #"Offers.plist"] ];
}
NSMutableDictionary *data002 = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
if ([fileManager fileExistsAtPath: path])
{
data002 = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
}
else
{
// If the file doesn’t exist, create an empty dictionary
data002 = [[NSMutableDictionary alloc] init];
}
int count;
for (count = 0; count < (int)[news count]; count++)
{
[data002 setObject:[[news objectAtIndex:count] objectForKey:#"imagePath"] forKey:[[news objectAtIndex:count] objectForKey:#"id"]];
[data002 writeToFile:path atomically:YES];
}
[data002 release];
}
Answer
What I did is instead of NSMutableDictionary I used NSMutableArray and all worked perfectly.

You plist is a dictionary, it has no order, just keys and values. If you need to store the order then change to use an array (of dictionaries) or use an additional file to store the order.
data002 is a dictionary. That's what makes your plist a dictionary (when you call writeToFile:). If you want to maintain the relative order you need to decide which approach you want to take and then create an NSArray of the items in order and use writeToFile: on the array to save it.

Typically, in order to maintain sequential data you would have to use an array of strings in the plist file rather than directly inserting strings to the root element (which is a dictionary). Alternatively, if you wish to store more complex entities, I would recommend indexing them and referring to the indexes alone in a separate array to maintain order.
Tap on the "+" sign to add an element, then select "Array" from the drop down menu under "type"
If you're doing this in code, simply insert an NSArray as an object in the NSDictionary you're saving as a plist:
[data002 setObject:#[path1,path2,path3] forKey:#"orderedArray"];

Related

local memory as well as webservice

even after so many research i haven't found a solution for this question. I am currently working on a app which uses 3 view controllers for Registration with a log out button. the last view controller has the Register button which saves all the details of registration in a web service. But if the user has filled the two view forms and logs out. The two view filled forms field should be saved in the local memory and wen the user logs it again the pre filled forms should load the fields saved in internal memory just to continue the Registration for webservice. Any idea how to implement this sort of functionality
As others have said, NSUserDefaults will suffice for what you need.
NSUserDefaults *registrationInfo = [NSUserDefaults standardUserDefaults];
Guessing you have text fields with the info you need. So pull out the text and save to a key like this.
[registrationInfo setObject:self.someTextFieldName.text forKey#"firstTextField"];
After repeating this for every text field(use different key names though), call this [registrationInfo synchronize];
To pull the data out, you open the defaults again just like the first line. And to retrieve a specific key: NSString *firstTextField = [registrationInfo objectForKey:#"firstTextField"];
To make this easier, you can also put all of your strings in an array or dictionary, and then add that as an object in your defaults. Then you only have to set/get once.
If you have large amount of data to save use CoreData else you NSUserDefaults to save it.
I suggest you to use PLIST There are mainly three steps to do this.
1) Generate .plist file.
NSError *error1;
BOOL resourcesAlreadyInDocumentsDirectory;
BOOL copied1;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath1 = [documentsDirectory stringByAppendingString:#"/epub.plist"];
resourcesAlreadyInDocumentsDirectory = [fileManager fileExistsAtPath:filePath1];
if(resourcesAlreadyInDocumentsDirectory == YES) {
} else {
NSString *path1 = [[[NSBundle mainBundle] resourcePath] stringByAppendingFormat:#"/epub.plist"];
copied1 = [fileManager copyItemAtPath:path1 toPath:filePath1 error:&error1];
if (!copied1) {
NSAssert1(0, #"Failed to copy epub.plist. Error %#", [error1 localizedDescription]);
}
}
2) Try to read(open) it.
NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath1];
3) write data to plist file.
[dict setObject:[NSNumber numberWithInt:value] forKey:#"value"];
[dict writeToFile:path atomically:YES];
This is a simple way to use it. I suggest to use .plist file in place of NSUserDefaults.

Append items to an existing key in NSDictionary saved in a plist

How can I append values to an existing key in NSDictionary from a plist?
What I have is basically a plist saved in disc with a few keys and values, the keys are the names of some students with one initial value. What I’m trying to do that doesn't work is to append more items to existing keys/Students by reading the plist, adding it to a temporary NSDictionary, appending a temporary array to an existing key in the plist but when I save the plist back it doesn’t work, it only saves the last two items and basically deletes the initial value for that key.
Here is the code that I’m using…
- (IBAction)testing:(id)sender
{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [dirPaths objectAtIndex:0];
NSString *fullPath = [documentsPath stringByAppendingPathComponent:#"studentsRecords.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
if(fileExists)
{
NSMutableDictionary *dictionaryRecords = [[NSMutableDictionary alloc]initWithContentsOfFile:fullPath];
NSLog(#"Items for Nathan: %#",[dictionaryRecords objectForKey:#"Nathan"]);// here the output is, Items for Nathan: Ruler
// which make sense since I only had one initial record for Nathan in my plist,
// Here, I want to add two more items to the key Nathan by appending an arry to
// NSMutableDictionary (dictionaryRecords) but it doesnt work
NSArray *tempArray = #[#"NoteBook", #"Pencil"];
[dictionaryRecords setObject:tempArray forKey:#"Nathan"];
[dictionaryRecords writeToFile:fullPath atomically:YES];
// when I check the plist after saving this, I only see the last two values, NoteBook and Pencil
// instead of the three that I'm expecting, Ruler, NoteBook and Pencil
}
}
What am I missing here?
Thanks a lot in advance.
[dictionaryRecords setObject:tempArray forKey:#"Nathan"] replaces whatever the previous value was with tempArray.
If you want to add to the existing array, you have to retrieve it, make a mutable copy, and append to it.
Here is how I made it work. I hope I didn't over complicated things here but for some reason I had to use two arrays, an NSArray and an NSMutableArray, I would think that the NSMutableArray would be enough but it didn't work.
- (IBAction)testing:(id)sender
{
// get directory path
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [dirPaths objectAtIndex:0];
// create plist
NSString *fullPath = [documentsPath stringByAppendingPathComponent:#"studentsRecords.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
// if file exists create dictionary
if(fileExists)
{
// add items from plist to dictionary
NSDictionary *dictionaryExistingRecords = [[NSDictionary alloc]initWithContentsOfFile:fullPath];
// make a copy if dictinary with existing items
NSMutableDictionary *dictionaryNewRecords = [dictionaryExistingRecords mutableCopy];
// array to hold existing values from a spcecified key
NSArray *arrayWithExistingKey = [dictionaryExistingRecords objectForKey:#"Nathan"];
// mutable array to add existing and be able to insert new items
NSMutableArray *arrayOldAndNewItems = [NSMutableArray arrayWithArray:arrayWithExistingKey];
// insert new items to array
[arrayOldAndNewItems addObject:#"Snow Pans"];
// add old and new items to specified key
[dictionaryNewRecords setObject:arrayOldAndNewItems forKey:#"Nathan"];
// save new records to plist
[dictionaryNewRecords writeToFile:fullPath atomically:YES];
}
}
#end

Save / Load IOS data

I've been attempting to get my app to save and load data from a set of arrays in the app, the odd issue is that when the app terminates (completely shut down) the data does not seem to load upon being restarted, i've had a look over a lot of posts, tutorials etc but i can't seem to get it to work, i have two test buttons on the app to trigger the save and load methods and also a button to clear the records, when i use these to test, it works perfectly and the data saves and loads correctly.
My current setup is as follows:
I have a plist file called data.plist in the supporting files directory, inside this file i have the various arrays with the same data in index 0 as the data that is initialised when the global data class creates an instance of itself.
My save code:
- (void)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"];
// create dictionary with arrays and their corresponding keys
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects: personalitySliderValue, looksSliderValue, humourSliderValue, chemistrySliderValue, emptySlider, notesValues,nameValues, noValues, ratingValues, nil] forKeys:[NSArray arrayWithObjects: #"personality", #"looks", #"humour", #"chemistry",#"empty",#"notes",#"name",#"no",#"rating", nil]];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check if plistData exists
if(plistData)
{
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
}
else
{
NSLog(#"Error in saveData: %#", error);
}
}
My load code:
- (void)loadData{
// 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 CHECK D capitalisation
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 list into dictionary object
NSDictionary *dictionaryTemp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!dictionaryTemp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
// assign values
personalitySliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"personality"]];
looksSliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"looks"]];
humourSliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"humour"]];
chemistrySliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"chemistry"]];
emptySlider = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"empty"]];
notesValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"notes"]];
nameValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"name"]];
noValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"no"]];
ratingValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"rating"]];
}
And finally the app delegate methods:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
// save the app data
[[GlobalData sharedGlobalData]saveData];
NSLog(#"save method run");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
// load the app data
[[GlobalData sharedGlobalData]loadData];
NSLog(#"load method run");
}
This has literally been making me pull my hair out, so any help would be great!
You can load data at launch time in this app delegate method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
....
[[GlobalData sharedGlobalData] loadData];
}

What is the correct way to save user data using NSFileManager?

I am having trouble initializing dictionaries I use throughout my program to store user achievements and scores.
I have almost identical code for the two dictionaries and only the gameCenterData dictionary seems to be working properly. I have tried altering the plist file name and contents yet nothing seems to make the playerData dictionary properly load info from the file as it should
In the Root View Controller I have the following code (playerData and gameCenterData are both NSMutableDictionaries and the plist files are in the proper place)
-(NSString *)scoreFilePath
{
NSArray *scorePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [scorePath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"PlayerScoreData.plist"];
}
-(NSString *)gameCenterFilePath
{
NSArray *gameCenterPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [gameCenterPath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"GameCenterData.plist"];
}
then the view did load
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *playerDataPath = [self scoreFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:playerDataPath])
{
playerData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"scoreData.plist"]];
[playerData writeToFile:[self scoreFilePath] atomically:YES];
NSLog(#"Player data file does not exist");
}
else
{
playerData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self scoreFilePath]];
NSLog(#"player data file exists");
}
NSLog(#"scoreData is %#",playerData);
NSString *gameCenterPath = [self gameCenterFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:gameCenterPath])
{
gameCenterData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"gameCenterData.plist"]];
[gameCenterData writeToFile:[self gameCenterFilePath] atomically:YES];
NSLog(#"game center data file does not exist");
}
else
{
gameCenterData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self gameCenterFilePath]];
NSLog(#"game center data file exists");
}
NSLog(#"gameCenterData is %#",gameCenterData);
the output is as follows
2012-08-05 11:46:49.991 GlobeRoller[6410:1be03] Player data file does not exist
2012-08-05 11:46:49.992 GlobeRoller[6410:1be03] playerData is (null)
2012-08-05 11:46:50.061 GlobeRoller[6410:1be03] game center data file does not exist
2012-08-05 11:46:50.062 GlobeRoller[6410:1be03] gameCenterData is {
"Career Odometer" = 0;
"Career Score" = 0;
"Cities Found" = 0;
"Offline Games Played" = 0;
"Online Games Played" = 0;
"Online Games Won" = 0;
}
I have searched all of the questions and answers to see if I can find out why this isn't working for both methods. Any help you could offer, or resources you could point me to I would greatly appreciate.
Thank you,
CF
The plist file you are trying to load from the bundle is either not there, or has been created improperly. Directly from the documentation of dictionaryWithContentsOfFile:.
Return Value
A new dictionary that contains the dictionary at path, or
nil if there is a file error or if the contents of the file are an
invalid representation of a dictionary.
You should make sure you are using the proper file name, and then open your plist in Xcode to see if it is properly formatted.
iOS is case sensitive. Are you sure that your file in the bundle is lower case, i.e. "#"scoreData.plist", and not upper case like the name your code uses? Also, verify that these two files are in your bundle - check the build phase or select the files (one at a time) and look in the 3rd Xcode pane in the file attribute section (to verify they are included in your target). If all that looks good then when you try to retrieve the files from your bundle:
Also, don't try to find the file at the root level of the bundle - you should be using:
NSString *path = [[NSBundle mainBundle] pathForResource:#"GameCenterData" ofType:#"plist"];
NSLog(#"PATH is %#", path);
...then use path instead of the code you are using now

conditionally create/read a .plist file

I want to check for the presence of a .plist file in the Documents directory. If it does not exist, I want to create it and seed it with the 1st entry. If it does exist I want to read it and append an entry to it.
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
// read Faves file...
NSMutableDictionary *tempDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
appDelegate.dictFaves = tempDict;
} else {
NSLog(#"Creating Favorites file");
BOOL result = [[NSFileManager defaultManager] createFileAtPath: path contents: (NSData *)appDelegate.dictFaves attributes: nil];
}
// .... and Append it to the list of existing favorites
[appDelegate.dictFaves setObject:newFave forKey:key];
createFileAtPath returns FALSE meaning the file was not created.
I question the validity of casting appDelegate.dictFaves to (NSDATA *). If that is unwise, how to I create a file with a Dictionary?
You can write it using:
-[NSDictionary writeToURL:atomically:]
So, you can say:
[appDelegate.dictFaves writeToURL:url atomically:YES];
(assuming appDelegate.dictFaves is a valid NSDictionary)

Resources