Error writing to plist in documents directory - objective-c

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

Related

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

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.

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.

Objective-C / Can't access (read) file in "Resources" folder

This is probably a very easy problem to solve but i have not succeeded. I have copied the file "questions_famquiz_easy_110326.txt" to the resources folder and now want to access (read) it and NSLog it to see that i am able to read it.
The code below is from one of the tests i have done based on examples that i have found on the web.
I am trying to access a txt-file in the Resources folder and fail to do so. I have searched for a solution, tested a lot, but not succeeded.
I am testing with the following code:
NSLog(#"\n \n >>>>>>viewDidLoad<<<<<<<<<");
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDirectory stringByAppendingPathComponent:#"questions_famquiz_easy_110326.txt"];
if(![fileManager fileExistsAtPath:filePath])
{
NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingString:#"/questions_famquiz_easy_110326.txt"]];
[data writeToFile:filePath atomically:YES];
}
NSString *myString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
NSLog(#"myString: %#", myString);
NSLog(#"\n \n>>>>>>DONE WITH viewDidLoad<<<<<<<<<");
exit(0); //====EXIT JUST FOR THE TEST====<<<
The above code is located in the first viewController that is started.
The output i get is:
>>>>>>viewDidLoad<<<<<<<<<
2011-04-24 21:53:47.825 FamQuiz_R0_1[542:307] myString: (null)
2011-04-24 21:53:51.044 FamQuiz_R0_1[542:307]
>>>>>>DONE WITH viewDidLoad<<<<<<<<<
Could someone nice help me to solve this?
update:
Here is the filePath:
2011-04-24 22:38:08.562 FamQuiz_R0_1[637:307] filePath: /var/mobile/Applications/7F5FDB03-0D22-46BC-91BC-4D268EB4BBEB/FamQuiz_R0_1.app/questions_famquiz_easy_110326.txt
Here is where i have placed the file:
PROBLEM SOLVED:
NSString *myString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
When i used the above it did not work, when i used the below it worked and printed out the text.
NSString *myString = [[NSString alloc] initWithContentsOfFile:filePath];
You're not reading from the resources folder. You're trying to read from the document directory. So getting back nil makes sense since the file doesn't exist there.
Change:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDirectory stringByAppendingPathComponent:#"questions_famquiz_easy_110326.txt"];
to:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"questions_famquiz_easy_110326" ofType:#"txt"];
and it should work for you.

Copying plist file to custom folder in ApplicationSupport (Objective-C)

I have this piece of code, which copies a plist file to the ApplicationSupport directory in the users folder.
NSString *resourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:kAutonumberPlist];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *dataPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:kAutonumberPlist];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dataPath]) {
[fileManager copyItemAtPath:resourcePath toPath:dataPath error:nil];
}
How ca I change it so that instead of copying the file into ~User/Library/ApplicationSupport, it will copy it into ~User/Library/ApplicationSupport/AnotherFolder. The "AnotherFolder" already exists by the way.
Thank you!
You are already using stringByAppendingPathComponent - you could just use it again.
For example:
NSString *dataPath = [[[paths objectAtIndex:0]
stringByAppendingPathComponent: #"AnotherFolder"]
stringByAppendingPathComponent: kAutonumberPlist];