how to edit a plist programmatically in xcode 5 iOS 7? - objective-c

how can I edit or change a value string from:
in this plist I need change or Set "NO" in Enabled Value from Item 2, I see a lot examples, but not working, or are deprecated, or don't use ARC, any idea or example code? please help guys
EDIT#1: I try this (not work)
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
path = [path stringByAppendingPathComponent:#"List.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:path]) {
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:CONTENT_DEFAULT ofType:PLIST];
[fileManager copyItemAtPath:sourcePath toPath:path error:nil];
}
NSArray* newContent = [[NSArray alloc]initWithContentsOfFile:path];
NSDictionary *Dict = [[NSDictionary alloc]initWithDictionary:[newContent objectAtIndex:2]];
[Dict setValue:#"NO" forKey:#"Enabled"];
[Dict writeToFile:path atomically:YES];
NOTE: not work, it crashes send me:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSDictionaryI 0x15de7ff0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key Enabled.', or Im wrong in something?
EDIT #2 new Try
NSString *path = [[NSBundle mainBundle] pathForResource:#"List" ofType:#"plist"];
NSString *savingPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSArray* newContent = [[NSArray alloc]initWithContentsOfFile:path];
NSMutableDictionary *Dict = [[NSMutableDictionary alloc]initWithDictionary:[newContent objectAtIndex:2]];
[Dict setValue:#"NO" forKey:#"Enabled"];
savingPath = [savingPath stringByAppendingPathComponent:#"Modified.plist"];
[Dict writeToFile:savingPath atomically:YES];
NSLog(#"newContent:%#",newContent);
NSArray *newnew = [[NSArray alloc]initWithContentsOfFile:savingPath];
NSLog(#"newnew:%#",newnew);
NOTE: now print me a crash:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (3) beyond bounds (2)'

All you have to do is to use NSMutableDictionary instead of a regular dictionary to modify stuff. You have to always check that the object you are editing is there.
To get the right path for the plist you created in Xcode you need to use:
NSString *path = [[NSBundle mainBundle] pathForResource:#"List" ofType:#"plist"];
You might actually want to save that file into the app's document directory. So you would use the following path for saving the content:
NSString *savingPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
savingPath = [savingPath stringByAppendingPathComponent:#"Modified.plist"];
[Dict writeToFile:savingPath atomically:YES];

Well, in your case the solution would be the following (when you have Dictionary as a member of Array):
NSString *path = [[NSBundle mainBundle] pathForResource:#"List" ofType:#"plist"];
NSArray *newContent = [[NSArray alloc]initWithContentsOfFile:path];
//iterating through all members since all of them has the string "YES" which should be replaced, otherwise, you will need to create a separate dictionary for each member
for ((i = 0; i < [newContent count]; i++)){
//here you take the member (dictionary) of the array
NSDictionary *dictOfArray = [newContent objectAtIndex:i];
[dictOfArray objectForKey:#"Enabled"]=#"NO";
}
//if you update the same pList you can use the same path (otherwise use another path)
[newContent writeToFile:path atomically:YES];
So, it works now.

Related

Objective-C: writing values to a specific index of an Array inside a .plist

I understand I can for instance write a value to a .plist file as such
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSString *comment = #"this is a comment";
[comment writeToFile:filePath atomically:YES];
But If i had for say an Array inside my .plist (gameArray) and I'd like to white comment into a particular index of my array i.e. gameArray[4] ; how would I do this ?
allow me to clarify
I have a plist: stored.plist
inside my plist there is an array gameArray
i would like to update specific indexes of gameArray inside the plist
is this possible ?
You cannot update and save data in application's main bundle instead you have to do in document directory or other directory like this:
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistFilePath = [documentsDirectory stringByAppendingPathComponent:#"stored.plist"];
if([[NSFileManager defaultManager] fileExistsAtPAth:plistFilePath])
{//already exits
NSMutableArray *data = [NSMutableArray arrayWithContentsOfFile:plistFilePath];
//update your array here
NSString *comment = #"this is a comment";
[data replaceObjectAtIndex:4 withObject:comment];
//write file here
[data writeToFile:plistFilePath atomically:YES];
}
else{ //firstly take content from plist and then write file document directory
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSMutableArray *data = [NSMutableArray arrayWithContentsOfFile:plistPath];
//update your array here
NSString *comment = #"this is a comment";
[data replaceObjectAtIndex:4 withObject:comment];
//write file here
[data writeToFile:plistFilePath atomically:YES];
}
Assuming the contents of 'stored.plist' is an array, you need to instantiate a mutable array from the path:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSMutableArray *array = [NSMutableArray arrayWithContentsOfFile:filePath];
NSString *comment = #"this is a comment";
// inserting a new object:
[array insertObject:comment atIndex:4];
// replacing an existing object:
// classic obj-c syntax
[array replaceObjectAtIndex:4 withObject:4];
// obj-c literal syntax:
array[4] = comment;
// Cannot save to plist inside your document bundle.
// Save a copy inside ~/Library/Application Support
NSURL *documentsURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] objectAtIndex:0];
NSURL *arrayURL = [documentsURL URLByAppendingPathComponent:[filePath lastPathComponent]];
[array writeToURL:arrayURL atomically:NO];

Error writing to plist in documents directory

I use the following code to copy a Resources plist file into the documents directory:
BOOL success;
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"Test-Info.plist"];
success = [fileManager fileExistsAtPath:filePath];
if (!success) {
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingFormat:#"Test-Info.plist"];
success = [fileManager copyItemAtPath:path toPath:filePath error:&error];
NSLog(#"Test-Info.plist successfully copied to DocumentsDirectory.");
}
I get the success message, which is great. I'm assuming it's been copied correctly into the documents folder.
However when I then try to read and write to the saved plist file it returns null:
Key entry in Test-Info.plist:
Key: EnableEverything
Type: Boolean
Value: YES
Write code:
NSString *adKey = #"EnableEverything";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [paths objectAtIndex:0];
NSString *path = [documentsDirectoryPath stringByAppendingPathComponent:#"Test-Info.plist"];
NSMutableDictionary *plist = [NSDictionary dictionaryWithContentsOfFile: path];
NSString *enableEverything = [[plist valueForKey:adKey] stringValue];
NSLog(#"****** EXISTING: %# ******", enableEverything); // returns (null)
// Disable in plist.
[plist setValue:0 forKey:adKey]; // will this work?
[plist writeToFile:path atomically:YES]; // this doesn't throw an error?
NSString *enableEverything1 = [[plist valueForKey:adKey] stringValue];
NSLog(#"****** NOW: %# ******", enableEverything1); // returns (null)
Output:
****** EXISTING: (null) ******
****** NOW: (null) ******
My question is why are they (null) when they exist within the plist file?
You are trying to mutate an immutable object.
You need a NSMutableDictionary.
IE
NSMutableDictionary *plist = [[NSDictionary dictionaryWithContentsOfFile: path] mutableCopy];
Also check, if the plist object isnt nil, as any messages can be send to nil without raiing an error. this wouldnt fail, also nothing actually happens.
[plist setValue:0 forKey:adKey]; // will this work?
[plist writeToFile:path atomically:YES]; // this doesn't throw an error?
as the source path is nil, you are most like not copying the file during compilation bundling. drag it here:
Try this for nsbundle path for resource
NSString *path= [[NSBundle mainBundle] pathForResource:#"SportsLogo-Info" ofType:#"plist"];
Try allocating
NSMutableDictionary *plist = [[NSMutableDictionary alloc] initWithDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
Also check if the file has been copied or not. Go to Library=>Application Support=>iPhone Simulator=>folder named your version of simulator iOS=>Applications=>Find the right folder of your project=>Documents see if the plist file is there

Why is my [dictionary objectForKey:#"TOSAcceptedValue"] null?

I'm writing to and reading from a plist in my document directory. First I write the file out if there is no file found. That seemed to work correctly because at first there was no file there and now there is and double-clicking on the file shows the expected content of a key called TOSAcceptedValue with a value of NO.
But if the file is found, which happened after I ran the above once, I try to read in that same value, I'm getting a null value. Here's the code, it may not be pretty as I've been hacking at it for a while to get it to function.
NSError *error;
NSString *TOSAcceptedStatus ;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog( #"paths is %#", paths);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"AppUsage.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
//first see if file exists. if it doesn't then write it out with a value of NO for Terms of Use Accepted
if (![fileManager fileExistsAtPath: path])
{
NSMutableDictionary *appUsageNo = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
//here add element to data file and write data to file
NSString *value = #"NO";
[appUsageNo setObject:#"TOSAcceptedValue" forKey:value];
[appUsageNo writeToFile: path atomically:YES];
[appUsageNo release];
//and set TOSAcceptedStatus to no since we know its a no right now
TOSAcceptedStatus = #"NO";
}
else { //file was found at expected location so let's see if they accepted Terms of Use already
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
NSLog( #"path is %#", path);
TOSAcceptedStatus = [dictionary objectForKey:#"TOSAcceptedValue"];
//NSLog(#"TOSAcceptedStatus is %#", TOSAcceptedStatus);
NSLog(#"TOSAcceptedStatus is %#", [dictionary objectForKey:#"TOSAcceptedValue"]);
[dictionary release];
}
and here's my Console results
2011-09-09 21:47:23.177 myApp[1027:207] paths is (
"/Users/user1/Library/Application Support/iPhone Simulator/4.3/Applications/34562D85-DBE9-4A4F-A142-JEFC1F4808D1/Documents"
)
2011-09-09 21:47:44.915 myApp[1027:207] path is /Users/user1/Library/Application Support/iPhone Simulator/4.3/Applications/34562D85-DBE9-4A4F-A142-JEFC1F4808D1/Documents/AppUsage.plist
2011-09-09 21:47:50.448 myApp[1027:207] TOSAcceptedStatus is (null)
Any clues why I can't get TOSAcceptedStatus back?
Also, am I allowed to write to and read from a plist after app started?
I think your main problem is that you have the object and key backwards:
[appUsageNo setObject:value forKey:#"TOSAcceptedValue"];

Reading values from a plist file

I have a UITextField and when the user hits a save button, the text should be saved into a plist file with the following code:
-(IBAction)saveButtonPressed
{
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
{
//5
NSString *bundle=[[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error]; //6
}
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
//here add elements to data file and write data to file
[data setObject:[inkTextField text] forKey:#"inkText"];
BOOL didWrite=[data writeToFile: path atomically:YES];
if(didWrite==YES)
NSLog(#"didWrite");
else NSLog(#"nope");
[data release];
}
Then I have a UITableView and I want to load the string value of plist into the cell text field with the following code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellViewController"];
if (cell == nil) {
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CellViewController" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
}
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
{
//5
NSString *bundle=[[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error]; //6
}
NSMutableDictionary *savedInks = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
//load from savedStock example int value
NSString *value;
value = [[savedInks objectForKey:#"inkText"]stringValue];
[savedInks release];
inkTitle.text=value;
return cell;
}
When I save, I get a the log outputs didWrite, so I know it saved properly. However when I visit the tableView, I get a crash with the following error:
2011-08-19 13:56:45.717 MyApp[36067:b303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString stringValue]: unrecognized selector sent to instance 0x4bb5fa0'
So I thought it was something to do with this line:
value = [[savedInks objectForKey:#"inkText"]stringValue];
so I tried
value = [savedInks objectForKey:#"inkText"];
but that also causes a crash but with no message. What am I doing wrong?
You should remove the 'stringValue', as you're already returning a NSString value.
In the next line, you're releasing 'savedInks', before retaining 'value'. Swap the release with the line below it and see if that works.:
NSString *value;
value = [savedInks objectForKey:#"inkText"];
inkTitle.text=value;
[savedInks release];
When savedInks is released, all of the objects within the dictionary are released as well. Setting inkTtile.text should perform a retain on value automatically.

copy all files with extension to documents directory

I am having a little bit trouble with copying files from an online place to the Documents Directory of the iPad in xcode.
The files that i want to download had to be the files with the extension 'xml'.
now i am doing this:
NSData *onlineLink = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://localhost:8888/"]];
NSString *extension = #"xml";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray *contents = [fileManager contentsOfDirectoryAtPath:onlineLink error:NULL];
NSEnumerator *e = [contents objectEnumerator];
NSString *filename;
while ((filename = [e nextObject])) {
if ([[filename pathExtension] isEqualToString:extension]) {
[fileManager copyItemAtPath:[documentsDirectory stringByAppendingPathComponent:filename toPath:documentsDirectory error:NULL]];
}
}
However this does not work, i get this error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteData fileSystemRepresentation]: unrecognized selector sent to instance 0xb000800'
Can anybody give me a hint why i am getting the error?
in the line:
NSArray *contents = [fileManager contentsOfDirectoryAtPath:onlineLink error:NULL];
contentsOfDirectoryAtPath should be an NSString, you are passing an NSData object