Changing values in plist - Doesn't change value - objective-c

I am using this simple method I found to write to a plist but it doesn't seem to work:
-(IBAction)modifyPlist:(id)sender{
NSBundle *mainBundle=[NSBundle mainBundle];
NSString *path=[mainBundle pathForResource:#"Preferences" ofType:#"plist"];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
[[dict objectForKey:#"Root"] setBool:YES forKey:#"startup"];
}
What am I doing wrong?
Thanks

You're loading the file into memory, and modifying that memory, but you're not writing it back to disk. You'll want to use NSDictionary's -writeToFile:atomically:
[dict writeToFile:path atomically:YES];
Reference link here.

I hope you know exactly what you're doing and this is (for whatever reason) your design decision -
because you're about to write to your app's bundle.
That'll certainly fail under Sandboxing and is generally a bad, bad idea.
If you've after storing user defaults please read up on the Mac way of storing preferences:
Preferences and Settings Programming Guide

// Make a path to the plist in the Documents directory (not the bundle directory)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Preferences.plist"];
// Then after creating or making changes to your dictionary, write your dictionary out to the plist file
[dict writeToFile:path atomically:YES];
Maybe look at:
Should I use NSUserDefaults or a plist to store data?

Related

How can i update plist file which has many group item in Objective-C?

I have a plist file like this, and now i want to edit the row.
For example, i want to edit row "ZFILMSEAT" in Item 1.
Please somebody suggest me how to update data in plist file.
First of all, When you retrieve the .plist file, store it as NSMutableDictionary.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"your plist name" ofType:#"plist"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
NSMutableArray *filmsPlaying = [[NSMutableArray alloc] iniWithArray:[dict objectForKey:#"FilmsPlaying"]];
NSMutableDictionary *filmToEdit = [filmsPlaying objectAtIndex:1];// IRL run a loop to get your desired film
NSString *newSeats = #"1-2-9";
[filmToEdit setObject:newSeats forKey:#"ZFILMSEAT"];
[filmsPlaying replaceObjectAtIndex:1 withObject:filmsToEdit];
[dict setObject:filmsPlaying forKey:#"FilmsPlaying"];
Here you have the film you want to edit. After you have made your edition to that dictionary, you need to write it back to App's document dir. (NOTE: You CANNOT save it back to your main bundle but you can write it to Documents instead).
NSString *pathForPlist = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
pathForPlist = [savingPath stringByAppendingPathComponent:#"your plist name.plist"];
[dict writeToFile:savingPath automically:YES];
To edit it in the main bundle without any programming, you can simply open it in any text editor or xcode itself and make your edition. Nothing techie about that.

How to save and load a NSString in a document based application?

I want to create a document based application in Xcode. It's a note app. People can create their notes file. It's a basic app, because I'm trying to learn how you can save and load data in a document based application. I'm not trying to save a tableview, I'm trying to save and load the stringValue of a TextField.
So, how do you save and load a NSString in a document based app in Objective-C (Xcode)?
You can use NSString's writeToFile:atomically:encoding:error:
and stringWithContentsOfFile:encoding:error: methods.
Here's an example of how to write to a file:
NSString *fileName = "myNote.txt";
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
NSString *filePath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:fileName]];
[myString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&myError];
Or if you want something advanced, you can use Core Data.

How to write data in .plist?

I'm trying to save some comments in a plist, that's OK cause its just a prototype. The problem is that i can read from plist but when I try to write and read after that, it throws an "array out of bounds" exception. I can't figure it out what I'm doing wrong here.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Comments" ofType:#"plist"];
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
NSMutableDictionary *newComment = [NSMutableDictionary dictionary];
[newComment setValue:commentTitle.text forKey:#"title"];
[newComment setValue:comment forKey:#"comment"];
[plistArray addObject:newComment];
[plistArray writeToFile:filePath atomically:NO];
That works fine, then i try to read:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Comments" ofType:#"plist"];
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
NSMutableDictionary *dictionary = (NSMutableDictionary *) [plistArray objectAtIndex:0];
NSLog(#"%#", [dictionary objectForKey:#"title"]);
And it throws the exception.
If I add the item manually to the plist, it works fine, i guess it means that my reading code its fine.
Could it be the structure of my plist?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
</array>
</plist>
* Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (1) beyond bounds (1)'
I added the "description" to the array before writing to the plist. If i use the following code:
NSString *aDocumentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// NSString *aFilePath = [NSString stringWithFormat:#"%#/Comments.plist", aDocumentsDirectory];
//
// NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:aFilePath];
The return is (null)
But if i use:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Comments" ofType:#"plist"];
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
i can see the contents of the array, and its all working properly.
The problem is: In both ways i cant write to the file, it keeps returning "NO". And i already checked the permissions
You are trying to write the file into mainBundle. Definitely not possible.
You will have to write the plist file to Documents or Application Support folder of the app.
Create File Path in Documents Directory :
NSString *aDocumentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *aFilePath = [NSString stringWithFormat:#"%#/Comments.plist", aDocumentsDirectory];
Write to FilePath
[plistArray writeToFile:aFilePath atomically:YES];
Read From FilePath
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:aFilePath];
I see two problems with your code:
(May or may not be a problem). If the file does not exist initially, the initWithContentsOfFile: selector will return nil, causing the rest of your code to be no-ops.
(Probably the cause). You may not write to the bundle resources directory. Store your file in the Documents or Caches directory instead.
To locate your documents directory, use something like this:
- (NSString*) pathForDocument:(NSString*)documentName {
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if(documentDirectories.count < 1) return nil;
return [[documentDirectories objectAtIndex:0] stringByAppendingPathComponent:documentName];
}
First of all, why are you writing a file into your bundle?
Then, to address your problem, check if you actually did write the file.
if ([plistArray writeToFile:filePath atomically:NO])
NSLog (#"Written");
else
NSLog (#"Not Written");
Also, log your array when you're read it using -(void)description to check the contents of the dictionary.
Edit
As you said that you're not writing to your plist. For now, just create a test plist on your desktop.
NSString *testPath = [[NSString stringWithString:#"~/Desktop/Comments.plist"] stringByExpandingTildeInPath];
if ([plistArray writeToFile:testPath atomically:NO])
NSLog (#"Written");
else
NSLog (#"Not Written");
If that still returns Not Written, then there's something wrong with your dictionary. Which I doubt because it's just strings (Though they could be placeholders for asking your question on stackoverflow. The docs states that the classes in the dictionary must be of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary). If that says written though, I'm guessing it doesn't write to your bundle because of permissions, which then you have to change your plist location to somewhere else other than your bundle, which I highly recommend.
If you only put one item in the array, you should obviously use index 0 instead of 1 when reading from it:
NSMutableDictionary *dictionary = (NSMutableDictionary *) [plistArray objectAtIndex:0];

Write in Main Bundle directory. Is it allowed?

I was pretty sure that writing in the main Bundle isn't possible in iOS ... for example an operation like :
NSString *path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
.....something
[xmlData writeToFile:path atomically:YES];
Why does the first example in Apple's documentation use this exact code?
The example is for OS X, which isn't quite as strict with permissions as iOS.
I'd be surprised if you are able to do that for much longer (if you can at all now) in a Mac App Store application bundle, though.
It could be worth filing a bug regarding the documentation.
That's not a link to the main bundle. That's a path to the resources folder, and a plist within that folder.
Hence the function name pathForResource...
Everything in the main bundle is cryptographically signed when you compile the app. The resources folder isn't though. You can write to and from that freely.
for #jrturton
// we need to get the plist data...
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Symptoms" ofType:#"plist"];
NSMutableArray *dataArray = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
// add a new entry
NSDictionary *addQuestion = [[NSDictionary alloc] initWithObjectsAndKeys:#"Blank.png",#"Icon",
[NSString stringWithFormat:#"%i",r],#"ID",
[titleTextField text],#"Title",
[questionTextField text],#"Text",
qnType,#"Type",
#"1",#"Custom",
[NSArray arrayWithObjects:#"Yes",#"No",nil],#"Values",
[unitsTextField text],#"Units",
nil];
[dataArray addObject:addQuestion];
[addQuestion release];
// rewrite the plist
[dataArray writeToFile:plistPath atomically:YES];

Saving a NSArray

I would like to save an NSArray either as a file or possibly use user defaults. Here's what I am hoping to do.
Retrieve already saved NSArray (if any).
Do something with it.
Erase saved data (if any).
Save the NSArray.
Is this possible, and if so how should I do this?
NSArray provides you with two methods to do exactly what you want: initWithContentsOfFile: and writeToFile:atomically:
A short example might look like this:
//Creating a file path under iOS:
//1) Search for the app's documents directory (copy+paste from Documentation)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//2) Create the full file path by appending the desired file name
NSString *yourArrayFileName = [documentsDirectory stringByAppendingPathComponent:#"example.dat"];
//Load the array
NSMutableArray *yourArray = [[NSMutableArray alloc] initWithContentsOfFile: yourArrayFileName];
if(yourArray == nil)
{
//Array file didn't exist... create a new one
yourArray = [[NSMutableArray alloc] initWithCapacity:10];
//Fill with default values
}
...
//Use the content
...
//Save the array
[yourArray writeToFile:yourArrayFileName atomically:YES];
You could implement NSCoding on the objects the array contains and use NSKeyedArchiver to serialize/deserialize your array to disk.
BOOL result = [NSKeyedArchiver archiveRootObject:myArray toFile:path];
The archiver will defer to your NSCoding implementation to get serializable values from each object and write a file that can be read with NSKeyedUnarchiver:
id myArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
More info in the serialization guide.
This would seem to be a problem most suited to Core Data as this will deal with all the persistent object data. When you retrieve you data it will return an NSSet, which is unsorted so you will have to have some way of sorting the data in the array such as a unique id number assocaited with each object you create.