Cant write data to plist file in - objective c - ios

I'm trying to read and write data to my plist file, While the reading part goes well, the writing part does nothing.
I might be mistaken for the writing part - i can't see any changes in my file under my bundle - it is still empty after my changes, and when i close the app and open it again - i still see empty email address line.
The code for writing (and placing the plist in the document folder for future writings)
NSFileManager *fileManger=[NSFileManager defaultManager];
NSError *error;
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *destinationPath= [doumentDirectoryPath stringByAppendingPathComponent:#"userData.plist"];
NSLog(#"plist path %#",destinationPath);
if ([fileManger fileExistsAtPath:destinationPath]){
NSLog(#"database localtion %#",destinationPath);
//return;
}
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"userData.plist"];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: sourcePath];
NSString *emailAddress = (NSString *)[dict objectForKey: #"emailAddress"];
if([emailAddress isEqualToString:#""])
{
// Do stuff
}
And for writing
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath =[pathsArray objectAtIndex:0];
NSString *destinationPath = [doumentDirectoryPath stringByAppendingPathComponent:#"userData.plist"];
NSMutableDictionary *plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile: destinationPath];
[plistDict setValue:#"myEmail#gmail.com" forKey:#"emailAddress"];
[plistDict writeToFile:destinationPath atomically: YES];
But as i said, nothing is changed in the file itself, and not even when i save, close the app and open it again (the string is always empty on my reading part)
Any help will be more than welcomed.

Related

write to plist - error [duplicate]

I have created save.plist in a resource folder. I have written some data within that directly (without using coding). I am able to read that data but I'm not able to write through code to the same save.plist. By using following code I am trying to write the data but it gets stored within my .app plist.
The code is here
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"save" ofType:#"plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSMutableDictionary *temp = (NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format errorDescription:&errorDesc];
if (!temp) {
NSLog(errorDesc);
[errorDesc release];
}
// [temp setValue:#"123" forKey:#"line1"];
// [temp writeToFile:plistPath atomically: YES];
//Reading data from save.plist
NSLog([temp objectForKey:#"name"]);
NSLog([temp objectForKey:#"wish"]);
NSNumber *num=[temp valueForKey:#"roll"];
int i=[num intValue];
printf("%d",i);
//writitng the data in save.plist
[temp setValue:#"green" forKey:#"color"];
[temp writeToFile:plistPath atomically: NO];
NSMutableDictionary *temp1 = (NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format errorDescription:&errorDesc];
NSLog([temp objectForKey:#"color"]);
I want that, the data which I want to write should get written into save.plist only which is stored in references. I am new with this concept. So if anyone knows it please help me.
Thanks in advance.
:-)
I don't know if I understand your question, but if you want to write into a .plist within your .app bundle you are probably doing something wrong. If you want to store preferences, you should consider using NSUserDefaults.
If you really want to modify a bundled .plist - here is some code:
NSString *plistPath = nil;
NSFileManager *manager = [NSFileManager defaultManager];
if (plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"Contents/Info.plist"])
{
if ([manager isWritableFileAtPath:plistPath])
{
NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
[infoDict setObject:[NSNumber numberWithBool:hidden] forKey:#"LSUIElement"];
[infoDict writeToFile:plistPath atomically:NO];
[manager changeFileAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] atPath: [[NSBundle mainBundle] bundlePath]];
}
}
Update:
Nate Flink pointed out that some of the NSFileManager methods used above are deprecated.
He posted an answer with the replacement methods below:
https://stackoverflow.com/a/12428472/100848
Updated version of the original awesome example by weichsel (thank you!). Xcode threw a couple warnings one of which is a deprecated method on NSFileManager. Updated here with non-deprecated methods from iOS 5.1
NSString *plistPath = nil;
NSFileManager *manager = [NSFileManager defaultManager];
if ((plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"mySpecial/PathTo.plist"]))
{
if ([manager isWritableFileAtPath:plistPath])
{
NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
[infoDict setObject:#"foo object" forKey:#"fookey"];
[infoDict writeToFile:plistPath atomically:NO];
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:[[NSBundle mainBundle] bundlePath] error:nil];
}
}
When you build the app, it will create an executable file "appName.app" and all the files are built in the bundle. Therefore, you can't access to resource folder when the app is running because all the data is in the bundle(not in folder).
However, you can access to a temp folder which contains some information of the app.
You can find the temp folder here:
Open finder--click on your username(under PLACES)--Library--Application Support--iPhone Simulator--User--Applications--(here you can find all the temp folders of your iPhone apps)
You can access to this temp folder by:
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
If you name your file save.plist, you can access to it like this:
NSString *filePath = [documentsDirectory stringByAppendingString:#"_save.plist"];
Then you just save your file to this filePath and it will appear in the temp folder named "Documents_save.plist".
*Note that the temp folder's name varies every time you run the app.
Recommend a book for you: 《Beginning iPhone Development--Exploring the iPhone SDK》. In Chapter 11 you can find what you want.
To summarize some of the other answers:
You're problem is that you're trying to write the file back into the folder that contains your application. That folder is not writable at runtime. Everything you're doing is fine, you just need to pick a different location to write your file to.
You can use the NSSearchPathForDirectoriesInDomains function to find a more suitable folder for this data. (Such as the #"Documents" folder.)
try this:
-(void)add:(NSRunningApplication *) app {
if ([self contains:app]) return;
[self.apps addObject:app.localizedName];
[self.apps writeToFile:self.dataFile atomically:YES];
}
from "Cocoa Programming".
you have to copy your plist into document directory...
because you cannot save anything without saving into document file....when you copied it will allow to write/modify on plist

GDataXML updating xml data

I am trying to update an xml file. After suggestion I ended up choosing GdataXml.
So I am trying to update options.xml file.
Original File
<Dat>
<Name>Tom</Name>
<Option>1</Option>
</Dat>
I need to change "Tom" to "Jim" and save in the same file
Here is the code I tried.
-(void)saveToXML
{
NSString* path = [[NSBundle mainBundle] pathForResource:#"options" ofType:#"xml"];
NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:path];
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error];
GDataXMLElement *rootElement = [GDataXMLElement elementWithName:#"Dat"];
NSArray *mySettings = [doc.rootElement elementsForName:#"Dat"];
for (GDataXMLElement *mySet in mySettings)
{
NSString *name;
NSArray *names = [mySet elementsForName:#"Name"];
if (names.count > 0)
{
GDataXMLElement *childElement = (GDataXMLElement *) [names objectAtIndex:0];
name = childElement.stringValue;
NSLog(childElement.stringValue);
[childElement setStringValue:#"Jim"];
}
}
[xmlData writeToFile:path atomically:YES];
}
But this is not updating options.xml file. Can some one help on this ?
The missing line of code you are looking for is
NSData *xmlData = doc.XMLData;
from Anupdas answer on your last question. You are currently reading a file into memory, initializing a new object using that memory, updating that new object and then writing the original file's memory into the new file location. So essentially you are reading a file and then writing the same file back to the file location.

how to store an image path in a plist?

I know this is probably a silly question but I'm storing most of my game data in a plist - with that I'd like to include references to images used within my game - same hierarchal level as 'supporting files'. I have different types of images stored in 3 separate folders. One folder for example is called imageclue. How could I store the path in my plist, I'm stuck because I can't just store the path in my plist as string - filename.jpg. I've tried getting the path of the file but when I log it out it .
Sorry if I'm not explaining well and thank you in advance for any help :)
EDIT**
I have a plist file added to my program I don't want to programatically add to it as the images are constants - the screenshots below show a tutorial instead of the filename.jpg (because that won't work seen as my images are stored in a file) I wondered what path name do I use as a string.
The image is from a tutorial off of appcoda.com - where it says thumbnails are the image path files. If you look at where the images are stored on the left - they are stored with the program files. My images are in a folder in there so I'm confused as to what to enter in my plist for the image file.
Hope this clears up what I meant, sorry :)
Store three variables in .h file
#interface YourViewController : UIViewController
{
NSString *folder1;
NSString *folder2;
NSString *folder3;
}
in viewdidload:
-(void) viewdidLoad
{
//get the documents directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//getting the folder name:
folder1 = [NSString stringWithFormat:#"%#/imageclue",
documentsDirectory];
folder2 = [NSString stringWithFormat:#"%#/folder2",
documentsDirectory];
folder3 = [NSString stringWithFormat:#"%#/folder3",
documentsDirectory];
}
-(NSArray*) getPlistFromFolder:(NSString*)folder imageName:(NSString*)image
{
NSString *imageTitle = [NSString stringWithFormat:#"%#/image",
folder];
NSArray *data = [[NSArray alloc] initWithContentsOfFile:plistName];
return data;
}
So in the plist file, just store the image name.
Hope this helps...
Do it like this,
NSDictionary *imagePaths = #{#"image 1": [NSHomeDirectory() stringByAppendingPathComponent:#"image 1"]};
[self writeToPlist:imagePaths];
- (void)writeToPlist:imagePaths:(id)plist{
NSError *error;
NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist format:kCFPropertyListXMLFormat_v1_0 options:0 error:&error];
if(error){
NSLog(#"Could not write to file");
return;
}
[data writeToFile:[self plistPath] atomically:YES];
}
Like wise loading is simple as this;
[self loadImagePathForImageNamed:#"image 1"];
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
}
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
NSData *data = [NSData dataWithContentsOfFile:[self plistPath]];
NSString *error;
NSPropertyListFormat format;
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if(error){
NSLog(#"Could not open plist %#", error);
return nil;
}
return dictionary[imageName];
}
You may have to handle the error when the file is not there by creating a new one, otherwise this should work.
You are storing path right way, just need to store filename of image with extension in plist when your images are in your Application Bundle, for more reference you can define key name Instead "item1", "item2" in your plist.
Now coming to actual Question, how to access image from plist
Step 1 : Read your recipes.plist from Application Bundle
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"recipes" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
Step 2 : Now Get Image/Thumbnails name out of it, which you want to load
Step 3 : Define following Function in your Controller, which returns image from name
- (UIImage *)getImageWithName:(NSString *)imageFileName
{
NSString *ext = [imageFileName pathExtension];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:[imageFileName stringByDeletingPathExtension] ofType:ext];
return [UIImage imageWithContentsOfFile:imagePath];
}
HOW TO USE
Suppose you want to load Image with key "Item2" then write following code
NSString *imageFileName = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item2"];
UIImage *item2Image = [self getImageWithName:imageFileName];
For "Item6"
NSString *imageFileName1 = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item6"];
UIImage *item6Image = [self getImageWithName:imageFileName1];

iOS plist is null

I know there are a multitude of questions about this on SO but I can't see where I am making the mistake and am hoping some extra eyes will help. I've verified the plist is in my bundle and it is also in my docs directory and it contains data. Here's a screen capture of the app package with the plist at top:
I pass the plist in from another class and have verified that it is the correct plist.
Here's my code:
-(id)init {
if (self = [super init]) {
//set up the appTracker
appTracker = [[OAI_AppTracker alloc] init];
//set up a file manager and error container
fileManger=[NSFileManager defaultManager];
//docs directory path
documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//track event
appTracker.appEvent = #"File Manager initialized";
[appTracker recordEvent];
}
return self;
}
- (NSDictionary* ) readPlist {
NSError* error;
//set up dictionary to hold our app data
NSDictionary* appData;
//set up destination path
NSString* destinationPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#", plistToRead]];
if ([fileManger fileExistsAtPath:destinationPath]){
//read plist
appData = [[NSDictionary alloc] initWithContentsOfFile:destinationPath];
} else {
//file doesn't exist so we have to move it to the doc folder
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:plistToWrite];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
//now read the plist
appData = [[NSDictionary alloc] initWithContentsOfFile:destinationPath];
}
NSLog(#"%#", appData);
return appData;
}
My log shows NULL instead of the data in the plist. Appreciate any help as to what I am doing wrong.
To read your plist try something like this:
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"PlistFileName" ofType:#"plist"];
NSDictionary *plistData = [[NSDictionary alloc] initWithContentsOfFile:plistPath];

iOS release not working as expected

I am using this code to get book names from a config.plist file. However my memory management is problematic. The '[dict release]' breaks the app completely and it exits.
The code works when the '[dict release]' is removed but it causes memory leaks as far as I can tell.
bnames is a global NSMutableArray
What am I doing wrong?
- (NSString *)loadBookname: (NSInteger) bookToLoad {
bookToLoad = [self bookOrder:bookToLoad];
//---get the path to the property list file---
plistFileNameConf = [[self documentsPath] stringByAppendingPathComponent:#"Config.plist"];
//---if the property list file can be found---
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileNameConf]) {
//---load the content of the property list file into a NSDictionary object---
dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileNameConf];
bnames = [dict valueForKey:#"BookNames"];
[dict release];
}
else {
//---load the property list from the Resources folder---
NSString *pListPath = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
dict = [[NSDictionary alloc] initWithContentsOfFile:pListPath];
bnames = [dict valueForKey:#"BookNames"];
[dict release];
}
plistFileNameConf = nil;
NSString *bookNameTemp;
bookNameTemp = [bnames objectAtIndex:bookToLoad - 1];
NSLog(#"bookName: %#", bookNameTemp);
return bookNameTemp;
}
You need to allocate your array properly:
bnames = [[NSArray alloc] initWithArray:[dict valueForKey:#"BookNames"]];
Double check that your dict returns the right data type.
There does not appear to be anything wrong with the way you allocate NSDictionary (although you could also use the [NSDictionary dictionaryWithContentsOfFile:] and save yourself having to worry about the release.
Either way I would suggest the issue is not with the [release] but probably the line BEFORE release:
bnames = [dict valueForKey:#"BookNames"];
a) Where is that allocated. I don't see an allocation or declaration of it anywhere?
b) What type of value do you expect back?
Put a break point on it and make sure your getting what you expect or anything.
If dict is not already a strong property, make it one. Then, use self.dict when assigning to it (and keep the release).
I've found what appears to be a better solution to the issue. This lets iOS manage the memory.
//---finds the path to the application's Documents directory---
- (NSString *) documentsPath {
NSLog(#"Start documentsPath");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
// NSLog(#"Found documentsPath 40");
NSLog(#"End documentsPath");
return documentsDir;
}
- (NSString *) configPath {
NSLog(#"Start configPath");
NSString *plistFileNameConf = [[self documentsPath] stringByAppendingPathComponent:#"Config.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistFileNameConf]) {
plistFileNameConf = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
}
NSLog(#"plistFile: %#",plistFileNameConf);
NSLog(#"End configPath");
return plistFileNameConf;
}
The following calls the above code as necessary:
NSString *Choice;
NSArray *properties;
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:[self configPath]];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!temp) {
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
Choice = [temp objectForKey:#"Choice"];
properties = [temp objectForKey:Choice];

Resources