writeToFile:atomically: not saving new plist data - ios

I am using writeToFile:atomically: to update the value of a key in my plist by 1 every time the app is launched. I put this code in viewDidLoad, which reads the string value of the key, gets the numeric value of that string, increases it by 1, converts it back to a string, and writes that as the new string for that key, but when I read it again it seems to have not updated. I can't figure out what I'm doing wrong. I don't need a special framework for writeToFile:atomically:, do I?
Here is the code:
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"DaysLaunched.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"DaysLaunched" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath:path error:&error];
}
NSMutableDictionary *plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
NSMutableDictionary *data1 = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
NSString *currentNumberOfDays = [NSString stringWithFormat:#"%#",[plistDict objectForKey:#"numberOfDays"]];
NSLog(#"currentNumberOfDays = %#", currentNumberOfDays); //0
int days = [currentNumberOfDays intValue];
days ++;
currentNumberOfDays = [NSString stringWithFormat:#"%d", days];
[data1 setObject:[NSString stringWithFormat:#"numberOfDays"] forKey:currentNumberOfDays];
NSLog(#"currentNumberOfDays = %#", currentNumberOfDays); //1
[data1 writeToFile: path atomically:YES];
currentNumberOfDays = [NSString stringWithFormat:#"%#",[plistDict objectForKey:#"numberOfDays"]];
NSLog(#"currentNumberOfDays = %#", currentNumberOfDays); //0 ??????? writeToFile isn't working?
And here is a screenshot of "DaysLaunched.plist" in my 'Supporting Files' folder, (I've also verified that the plist file name is spelled exactly the same way I spelled it in my code, via Copy-Paste)
The original plist file is also in my 'Copy Bundle Resources' in targets.

Try
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"DaysLaunched.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"DaysLaunched"
ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath:path error:&error];
}
NSMutableDictionary *plistDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
NSInteger days = [plistDict[#"numberOfDays"] integerValue];
plistDict[#"numberOfDays"] = [#(++days) stringValue];
[plistDict writeToFile: path atomically:YES];

You have made a mistake while setting your data.
You should do:
[data1 setObject:currentNumberOfDays forKey:#"numberOfDays"];
instead of:
[data1 setObject:[NSString stringWithFormat:#"numberOfDays"] forKey:currentNumberOfDays];

Related

Saving image and its title on plist

How can I store an image and any string value in plist as dictionary.I
have created a plist file in the project,and
also how can i append the file with same dictionary of different image and name?
Use the following code to write image & title to-gather in plist-
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"PlistName.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
path = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat: #"PlistName.plist"] ];
}
fileManager = [NSFileManager defaultManager];
NSMutableDictionary *dataDictionary=[NSMutableDictionary alloc] init];
[dataDictionary setObject:ImageData forKey:image];
[dataDictionary setObject:ImageTitle forKey:title];
[dataDictionary writeToFile: path atomically:YES];
And read your image & title dictionary from plist like this-
- (NSDictionary * )readDataFromPlist()
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"PlistName.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableDictionary *dataDictionary;
if ([fileManager fileExistsAtPath: path])
{
dataDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
return dataDictionary;
}else
return nil;
}
You can follow this link. It provides detail how to store image to plist
Storing image in plist
And for title to store you can add new key like below.
// Get a full path to a plist within the Documents folder for the app
NSString *titleStr = #"Title Of Image";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [NSString stringWithFormat:#"%#/YOURPLIST.plist",
[paths objectAtIndex:0]];
// Place an image in a dictionary that will be stored as a plist
[dictionary setObject:image forKey:#"image"];
[dictionary setObject:titleStr forKey:#"title"];
// Write the dictionary to the filesystem as a plist
[NSKeyedArchiver archiveRootObject:dictionary toFile:path];

Accessing Plist from user documents instead of NSBundle

Hello I have the following code which works perfect, it finds the Plist and assigns the NSDictionary to my NSArray called landingSites. The only problem is I want the user to be able to add sites so need to store the Plist in the user documents instead of the NSBundle:
plistPath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
dictionaryKeys = [newDictionary allKeys];
self.landingSites = [dictionaryKeys sortedArrayUsingSelector:#selector(compare:)];
self.dictionaryFromPlist = newDictionary;
[Picker selectRow:0 inComponent:0 animated:NO];
for(NSString *parent in dictionaryKeys)
{
NSDictionary *parentData = [dictionaryFromPlist objectForKey:parent];
NSArray *child = [parentData objectForKey:#"Name"];
NSLog(#"%#", child);
}
So far I have produced the code below, but whenever I run the program, the if-statement ALWAYS executes as if the plist in never copied to the user documents. Also I do not know how to attach the NSDictionary contents to my landingSites array as I did above. Please help
(void) viewDidLoad {
[super viewDidLoad];
NSError *error;
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Data.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:path]) {
NSLog(#"copying database to users documents");
NSString *pathToSettingsBundle = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
[fileManager copyItemAtPath:pathToSettingsBundle toPath:path error:&error];
}
else{
NSLog(#"users database already configured");
}
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask,YES);
NSDocumentationDirectory is wrong. You are looking for NSDocumentDirectory or NSLibraryDirectory.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

NSCFArray objectForKey crash?

I am trying to do something relatively simple. I have a .plist in my bundle and I am trying to save it to the documents directory with encryption. Now before I tried added encryption, it worked fine. However a new crash has arose.
This is how I save my .plist:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Hi.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path]) {
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"GameSave" ofType:#"plist"];
NSData *plistData = [NSData dataWithContentsOfFile:bundle];
[NSKeyedArchiver archiveRootObject:plistData toFile:path];
plistData = [plistData AES256EncryptWithKey:#"536335"];
[plistData writeToFile:path atomically:YES];
}
Then this is how I retrieve my .plist (and later change a value and re-save)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Hi.plist"];
//Get array and then current level dict
NSData *plistData = [[NSData alloc] initWithContentsOfFile:path];
plistData = [plistData AES256DecryptWithKey:#"1111"];
NSMutableArray *savegameArray = [[NSKeyedUnarchiver unarchiveObjectWithData:plistData] mutableCopy];
int objectIndex = [Singleton sharedInstance].levelNumber - 1;
NSMutableDictionary *levelDict = [[savegameArray objectAtIndex:objectIndex] mutableCopy];
[levelDict setObject:videoID forKey:#"RecordingURL"];
//Now put new dict back in array
[savegameArray replaceObjectAtIndex:objectIndex withObject:levelDict];
NSData *savedarrayData = [NSKeyedArchiver archivedDataWithRootObject:savegameArray];
savedarrayData = [savedarrayData AES256EncryptWithKey:#"1111"];
[savedarrayData writeToFile:path atomically:YES];
However, in the read code every time I get to this line: NSMutableArray *savegameArray = [[NSKeyedUnarchiver unarchiveObjectWithData:plistData] mutableCopy]; There is a SIGABRT crash which prints:
'-[__NSCFArray objectForKey:]: unrecognized selector sent to instance
0x16604140'
What am I doing wrong?
As we discovered through our chat, we had to first convert the decrypted NSData object to a proper NSPropertyListSerialization serialization.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Hi.plist"];
NSData *plistData = [[NSData alloc] initWithContentsOfFile:path];
NSData *newData = [plistData AES256DecryptWithKey:#"1111"];
NSPropertyListFormat format;
NSMutableArray *savegameArray = [[NSPropertyListSerialization propertyListFromData:newData mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:nil]mutableCopy];
NSLog(#"Array: %#",savegameArray);
You just need to allocate memory to your NSMutableArray like...
NSMutableArray *arrayName = [[NSMutableArray alloc] init];
Until you do that crash will arose..
Hope this is helpful.
Thanks,
Rajesh..

Saving and reading objects from plist cause huge memory of 2G

I have a system to save lots of images to plist , that works good, except 2 main problems.
first, when it start the process ,memory on xcode goes to 2G ! (and down when done)
Second, it takes too long(10+ seconds for 100 images)compared to NSUserdefaults, which i told is slower than this .
I am archiving the data first.
What am i doing wrong for having so much memory and so slow saving ?
-(void)saveToFileWithData:(NSMutableDictionary*)dic
{
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error];
}
NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:dic];
BOOL sucess=[myData writeToFile:path atomically:YES];
if(sucess)
NSLog(#"saved:%lu",(unsigned long)[myData length]);
else
NSLog(#"failed:%lu",[myData length]);
myData=nil;
}
The reading is like this :
-(NSMutableDictionary*)readFromFile
{
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //1
NSString *documentsDirectory = [paths objectAtIndex:0]; //2
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data.plist"]; //3
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path]) //4
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"]; //5
[fileManager copyItemAtPath:bundle toPath: path error:&error]; //6
}
NSMutableDictionary *dic = [[ NSMutableDictionary alloc] init];
NSData *serialized = [NSData dataWithContentsOfFile:path];
//check first if file exist, than if it has content(empty file had 42 bytes-and crashes the archiver)
if ([[NSFileManager defaultManager] fileExistsAtPath:path] && [serialized length]>1000000)
dic = (NSMutableDictionary*) [NSKeyedUnarchiver unarchiveObjectWithData:serialized];
serialized=nil;
return dic;
}
Use them with :
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
dic = [self readFromFile];
//change dic
[self saveToFileWithData:dic];
There is no reason to copy the plist from the app bundle to the Documents directory just to read it.
A plist is not a good solution, in this case it is huge Put the images individual files and the image file names in the plist. Read the now small plist and then rest the images one by one. Reading each file individually uses a smaller amount of memory.
But, do you really want 500MB of images in memory all at once? After having read the plist of image information just read the images as needed, perhaps with a cache that purges images based on usage.

Adding Array to PLIST

I am trying to add and array to a Root array in my plist:
And is not working. Here's my code:
-(IBAction)addName:(id)sender{
NSArray *arrayValues = [NSArray arrayWithObjects: nameLabel.text, nameDate.text, nameValue.text, nil];
NSString *plistpath = [[NSBundle mainBundle] pathForResource:#"Names" ofType:#"plist"];
NSMutableArray *namesNew = [[NSMutableArray alloc] initWithContentsOfFile:plistpath];
[namesNew addObject:arrayValues];
[namesNew writeToFile:plistpath atomically:YES];
}
What am I doing wrong? Thanks!
You need to move the file to NSDocumentDirectory. Then edit the plist file.
For example:
Moving to NSDocumentDirectory:
-(NSDictionary *)copyBundleToDocuments
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *documentPlistPath = [documentsDirectory stringByAppendingPathComponent:#"Names.plist"];
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *bundlePlistPath = [bundlePath stringByAppendingPathComponent:#"Names.plist"];
//if file exists in the documents directory, get it
if([fileManager fileExistsAtPath:documentPlistPath])
{
NSMutableDictionary *documentDict = [NSMutableDictionary dictionaryWithContentsOfFile:documentPlistPath];
return documentDict;
}
//if file does not exist, create it from existing plist
else
{
NSError *error;
BOOL success = [fileManager copyItemAtPath:bundlePlistPath toPath:documentPlistPath error:&error];
if (success) {
NSMutableDictionary *documentDict = [NSMutableDictionary dictionaryWithContentsOfFile:documentPlistPath];
return documentDict;
}
return nil;
}
}
Then get the plist:
-(void)plistArray:(NSArray*)array
{
//get the documents directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//getting the plist file name:
NSString *plistName = [NSString stringWithFormat:#"%#/Names.plist",
documentsDirectory];
NSMutableArray *namesNew = [[NSMutableArray alloc] initWithContentsOfFile:plistName];
[namesNew addObject:arrayValues];
[namesNew writeToFile:plistName atomically:YES];
return nil;
}
The plist should be a dictionary as the base object instead of an array.
NSMutableDictionary *namesNew = [NSMutableDictionary dictionaryWithContentsOfFile:plistpath];
[namesNew setObject: arrayValues forKey: #"Root"];
[namesNew writeToFile:plistpath atomically:YES];
You cant write your plist to the bundle you need to use NSDocumentDirectory or NSCachesDirectory
Just copy your plist to bundle the overwrite it.
Note: learn the difference between NSCachesDirectory and NSDocumentDirectory
https://developer.apple.com/icloud/documentation/data-storage/
Copy your plist from bundle to documents(in below code caches), you need to this only one time if your plist in your bundle, I prefer using this code in appdelegate.m when - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Names.plist"];
NSString *plistInDocuments=#"Names.plist";
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:plistInDocuments];
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath]){
[[NSFileManager defaultManager] copyItemAtPath:sourcePath
toPath:dataPath
error:&error];
}
NSLog(#"Error description-%# \n", [error localizedDescription]);
NSLog(#"Error reason-%#", [error localizedFailureReason]);
Get your file and overwrite it
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistInDocuments=#"Names.plist";
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:plistInDocuments];
//add object here
NSMutableArray *namesNew = [[NSMutableArray alloc] initWithContentsOfFile:dataPath];
[namesNew addObject:arrayValues];
NSError *error = nil;
if ([myFile writeToFile:dataPath options:NSDataWritingAtomic error:&error]) {
// file saved
} else {
// error writing file
NSLog(#"Unable to write plist to %#. Error: %#", dataPath, error);
}

Resources