NSMutableDictionary data not saving in file for wrong file directory - ios

I have two UITextField
email
password
These two field values are stored in a NSMutableDictionary call userData. Now i want to save these two field values in a file, so that the records keep there and i can restore this record to check user login correctly. Here i want to accomplish that, but not working.
My code :
-(void) saveRegistrationData
{
userData = [[NSMutableDictionary alloc]
initWithObjects:[[NSMutableArray alloc] initWithObjects:#"123456", nil]
forKeys:[[NSMutableArray alloc] initWithObjects:#"admin#gmail.com", nil]];
[userData setObject:passwordTextField.text forKey:emailTextField.text];
NSString *savePath = [#"/MediaFiles/Documents/" stringByExpandingTildeInPath];
[userData writeToFile: savePath atomically: YES];
//[userData writeToFile:#"MediaFiles/Documents/" atomically:YES];
for (id key in userData)
{
NSLog(#"%# is for %#", key, [userData objectForKey:key]);
}
}
I think the path is not setting correctly. If any one similar with the solution, please share with me. Thanks in advanced. Have a good day.

It's not working for a few reasons.
1) You're writing to a folder in the root of the device's filesystem. Apple uses sandboxing to prevent this from happening as you could overwrite and modify any system files.
2) You're writing to a folder rather than a file. To write to a file, you need to specify a filename (and extension). i.e. "/MediaFiles/Documents/dnt_lk_at_dis.plist"
In order to fix these issues, you need to be firstly getting the path to the sandbox (documents directory) and then append the filepath.
NSString *docsPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filename = #"secret.plist";
NSString *filePath = [docsPath stringByAppendingPathComponent:filename];
Furthermore, I strongly suggest that you instead use the keychain for storing sensitive user information. Anybody with a jailbroken device, or anybody that has access to the file system will be able to extract the user's information if it is stored in plain-text. At the very least, please encrypt the password before writing it to disk.

-(void) saveRegistrationData
{
userData = [[NSMutableDictionary alloc] init];
[userData setObject:passwordTextField.text forKey:#"password"];
[userData setObject:emailTextField.text forKey:#"email"];
// Alternative to the above:
userData = [[NSMutableDictionary alloc]
initWithObjects:[[NSMutableArray alloc] initWithObjects:passwordTextField.text, emailTextField.text, nil]
forKeys:[[NSMutableArray alloc] initWithObjects:#"password", #"email" nil]];
NSString *savePath = [#"/MediaFiles/Documents/myDict.plist" stringByExpandingTildeInPath]; // write to a file, not a dictionary
[userData writeToFile: savePath atomically: YES];
for (id key in userData)
{
NSLog(#"%# is for %#", key, [userData objectForKey:key]); // now you should see the result that you want to.
}
// Alternative for the above - the lazy way of doing it:
NSLog (#"theDictionary: %#", userData);
}
Please forgive me any typos or so. I did not compile it for you :-)

Related

Read and Update plist in Objective-C

I would like to let users choose to store username or not at the login page, so my plist has 2 keys: userIsSaved(bool), and username(string)
Here's my script:
NSString *path = [[NSBundle mainBundle] pathForResource:#"userinfo" ofType:#"plist"];
NSMutableDictionary *plistDic = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
BOOL userIsSaved = [[plistDic objectForKey:#"userIsSaved"] boolValue];
NSString *username = plistDic[#"username"];
if (lg.saveUserSwitch.isOn){
if (userIsSaved == false){
[plistDic setObject:[NSNumber numberWithBool:YES] forKey:#"userIsSaved"];
plistDic[#"username"] = lg.usernameFld.text;
[plistDic writeToFile: path atomically: YES];
if ([plistDic writeToFile: path atomically: YES] == NO) {
NSLog(#"!!!Save plist file failed");
}
}
}
I got some questions:
Why can't I write plistDic[#"userIsSaved"] = true to update the bool value? how to properly update it? [plistDic setObject:[NSNumber numberWithBool:YES] forKey:#"userIsSaved"] seems to change the type of it. How should I read it?
I can't write the plistDic to the plist. I don't know what's the problem.
Answer
You can't store a boolean (or integer or float or ...) into a dictionary - only an object can be stored. So you need to convert it to an object first, in this case, NSNumber as you did. You can also shortcut it with
plistDic setObject:#(YES) forKey:#"userIsSaved"]
To write first get the path to the correct directory, which typically is something like
NSURL * dir = [[NSFileManager.defaultManager URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask].firstObject
URLByAppendingPathComponent:NSBundle.mainBundle.bundleIdentifier
isDirectory:YES];
Then you can write using something like
NSString * myFile = [dir.path stringByAppendingPathComponent:#"mylist.plist"];
and then write to myFile in stead.
Note also maybe a good idea to use URLs.

local memory as well as webservice

even after so many research i haven't found a solution for this question. I am currently working on a app which uses 3 view controllers for Registration with a log out button. the last view controller has the Register button which saves all the details of registration in a web service. But if the user has filled the two view forms and logs out. The two view filled forms field should be saved in the local memory and wen the user logs it again the pre filled forms should load the fields saved in internal memory just to continue the Registration for webservice. Any idea how to implement this sort of functionality
As others have said, NSUserDefaults will suffice for what you need.
NSUserDefaults *registrationInfo = [NSUserDefaults standardUserDefaults];
Guessing you have text fields with the info you need. So pull out the text and save to a key like this.
[registrationInfo setObject:self.someTextFieldName.text forKey#"firstTextField"];
After repeating this for every text field(use different key names though), call this [registrationInfo synchronize];
To pull the data out, you open the defaults again just like the first line. And to retrieve a specific key: NSString *firstTextField = [registrationInfo objectForKey:#"firstTextField"];
To make this easier, you can also put all of your strings in an array or dictionary, and then add that as an object in your defaults. Then you only have to set/get once.
If you have large amount of data to save use CoreData else you NSUserDefaults to save it.
I suggest you to use PLIST There are mainly three steps to do this.
1) Generate .plist file.
NSError *error1;
BOOL resourcesAlreadyInDocumentsDirectory;
BOOL copied1;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath1 = [documentsDirectory stringByAppendingString:#"/epub.plist"];
resourcesAlreadyInDocumentsDirectory = [fileManager fileExistsAtPath:filePath1];
if(resourcesAlreadyInDocumentsDirectory == YES) {
} else {
NSString *path1 = [[[NSBundle mainBundle] resourcePath] stringByAppendingFormat:#"/epub.plist"];
copied1 = [fileManager copyItemAtPath:path1 toPath:filePath1 error:&error1];
if (!copied1) {
NSAssert1(0, #"Failed to copy epub.plist. Error %#", [error1 localizedDescription]);
}
}
2) Try to read(open) it.
NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath1];
3) write data to plist file.
[dict setObject:[NSNumber numberWithInt:value] forKey:#"value"];
[dict writeToFile:path atomically:YES];
This is a simple way to use it. I suggest to use .plist file in place of NSUserDefaults.

Append New Input to plist in Documents Folder

After much searching of SO I've been able to come up with code to correct previous failures. What I am trying to accomplish is accepting user input from a UITextField and add that input to my TableView, which is populated from a plist. The root value of my plist is dictionary and I would like to keep it as such. My problem is when writeToFile is called, I'm completely over writing the existing values instead of inserting, adding or appending to the existing data. It doesn't appear that my attempt to combine the two dictionaries is working, since only the new value is being stored to the plist. Any insight as to where I'm going wrong? The following code is what I have
//////////********** Add New Cell When OK is Chosen
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
UITextField *newDevice = [alertView textFieldAtIndex:0];
NSLog(#"newDevice - %#",newDevice.text);
if (alertView.tag == 1 && buttonIndex == 1){
NSMutableDictionary *input = [[NSMutableDictionary alloc]init];
[input setObject:[NSArray arrayWithObject:newDevice.text] forKey:#"Room"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Test.plist"];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// Combine the two Dictionaries to create one
[dictionary addEntriesFromDictionary:input];
// Write Combined Dictionary to plist
[dictionary writeToFile:path atomically:YES];
// Add Newly Created text to Table because reload table doesn't do it
[myTableData addObject:newDevice.text];
// Reload Table Data even though it seems useless
[myTable reloadData];
}
}
I guess you overwrote the values of the key Room.
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Test.plist"];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// Get exist input from plist
NSMutableArray *inputFromPlist = dictionary[#"Room"];
// add new object to input
[inputFromPlist addObject:newDevice.text];
// set Room with modified object
[dictionary setObject:inputFromPlist forKey:#"Room"];
// Write Combined Dictionary to plist
[dictionary writeToFile:path atomically:YES];
// do something after saving plist ...

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

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];

Resources