iPhone read/write .plist file
Asked Answered
S

2

5

I'm making a application where I need to store some information the user have provided. I try to use a .plist file to store the information, I found this:

NSString *filePath = @"/Users/Denis/Documents/Xcode/iPhone/MLBB/data.plist";
NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
[plistDict setValue:@"Man" forKey:@"Gender"];
[plistDict writeToFile:filePath atomically: YES];

The problem is that the application will only work as long as I'm testing it in the iPhone simulator. I've tried this Changing Data in a Plist but without luck. I have also read something about that I need to add it to my bundle, but how?

New code:

- (IBAction)segmentControlChanged{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistLocation = [documentsDirectory stringByAppendingPathComponent:@"data.plist"];
NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistLocation];

if (Gender.selectedSegmentIndex == 0) {
    [plistDict setObject:@"Man" forKey:@"Gender"];
    [plistDict writeToFile:plistLocation atomically: YES];
}
else
{
    [plistDict setObject:@"Women" forKey:@"Gender"];
    [plistDict writeToFile:plistLocation atomically: YES];
}
}
Serb answered 17/2, 2012 at 21:12 Comment(0)
E
14

I guess you have added your plist file to your resources folder in Xcode (where we place image, if not then you need to place that first). Resources data goes to [NSBundle mainBundle] by default and iOS does not allow us to change data inside bundle. So first you need to copy that file to Documents Directory.

Here is the code for copying file from NSBundle to the Documents directory.

- (NSString *)copyFileToDocumentDirectory:(NSString *)fileName {
    NSError *error;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *documentsDir = [paths objectAtIndex:0];
    NSString *documentDirPath = [documentsDir
                                      stringByAppendingPathComponent:fileName];

    NSArray *file = [fileName componentsSeparatedByString:@"."];
    NSString *filePath = [[NSBundle mainBundle]
                                         pathForResource:[file objectAtIndex:0]
                                                  ofType:[file lastObject]];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL success = [fileManager fileExistsAtPath:documentDirPath];

    if (!success) {
        success = [fileManager copyItemAtPath:filePath
                                       toPath:documentDirPath
                                        error:&error];
        if (!success) {
        NSAssert1(0, @"Failed to create writable txt file file with message \
                                         '%@'.", [error localizedDescription]);
        }
    }

    return documentDirPath;
}

Now you can use the returned documentDirPath to access that file and manipulate (Read/Write) over that.

The plist structure is:

<array>
    <dict>key-value data</dict>
    <dict>key-value data</dict>
</array>

Here is code to write data into plist file:

/* Code to write into file */

- (void)addToMyPlist {
    // set file manager object
    NSFileManager *manager = [NSFileManager defaultManager];

    // check if file exists
    NSString *plistPath = [self copyFileToDocumentDirectory:
                                                       @"MyPlistFile.plist"];

    BOOL isExist = [manager fileExistsAtPath:plistPath];    
    // BOOL done = NO;

    if (!isExist) {
        // NSLog(@"MyPlistFile.plist does not exist");
        // done = [manager copyItemAtPath:file toPath:fileName error:&error];
    }
    // NSLog(@"done: %d",done);

    // get data from plist file
    NSMutableArray * plistArray = [[NSMutableArray alloc]
                                           initWithContentsOfFile:plistPath];

    // create dictionary using array data and bookmarkKeysArray keys
    NSArray *keysArray = [[NSArray alloc] initWithObjects:@"StudentNo", nil];
    NSArray *valuesArray = [[NSArray alloc] initWithObjects:
                                   [NSString stringWithFormat:@"1234"], nil];

    NSDictionary plistDict = [[NSDictionary alloc]
                                                  initWithObjects:valuesArray
                                                          forKeys:keysArray];

    [plistArray insertObject:poDict atIndex:0];

    // write data to  plist file
    //BOOL isWritten = [plistArray writeToFile:plistPath atomically:YES];
    [plistArray writeToFile:plistPath atomically:YES];

    plistArray = nil;

    // check for status
    // NSLog(@" \n  written == %d",isWritten);
}
Eucken answered 18/2, 2012 at 5:19 Comment(11)
What am I going to do with this code? I've tried just paste it, but it didn't work. And do i need to put the filename somewhere?Serb
Where is the resources folder?Serb
This code is to get path for plist file, that is nothing but the "plistLocation" in your code. First add you plist files, with your images. Then call this method to get the plist file path, store it in your "plistLocation" variable and rest is fine with your codeEucken
So what do i need to write instead of plistLocation when using this code? And what do i need to do with the code, just paste it in my viewController.m ?Serb
yes add this method in your viewController. call this method to get plist path. - (IBAction)segmentControlChanged{ NSArray paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; plistLocation = [self copyFileToDocumentDirectory:@"data.plist"]; NSMutableDictionary plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistLocation]; [plistDict setValue:@"Man" forKey:@"Gender"]; [plistDict writeToFile:filePath atomically: YES];Eucken
Now i have this: NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; plistLocation = [ self copyFileToDocumentDirectory:@"data.plist"]; NSMutableDictionary plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistLocation]; [plistDict setValue:@"Man" forKey:@"Gender"]; [plistDict writeToFile:filePath atomically: YES]; it says that copyFileToDocument directory isn't found as a error in third line. What to do? :-)Serb
@Mrunal, is this code also working if Documents folder has a subfolders? I have tried this but when I created a subfolder it didn't work anymore.Varner
@julie: You require to do some twink in first function.. NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);NSString *documentsDir = [paths objectAtIndex:0];NSString *documentDirPath = [documentsDir stringByAppendingPathComponent:fileName]; Over here update the path as per your folder name. You can pass your folder name to function and append to documentDir string in proper formatEucken
@Mrunal, actually, I just want the file to be in Documents folder. But what happens is that after this line: NSString *filePath = [[NSBundle mainBundle] pathForResource:[file objectAtIndex:0] ofType:[file lastObject]]; filePath is nil. but file contains array of strings.Varner
Thank you for this answer! I do have a quick question... in the "addToMyPlist" method why are you setting the plistArray variable to nil at the end? Wouldn't the reference be set to nil once the program has left the scope of that method?Bejarano
@Bejarano : Earlier there was no ARC concept, developer has to deallocate the object for which one has ownerships. So once we nullify that reference, it means that particular object memory has no references assigned, so that it will be deallocated. Now there is ARC available, so no need to do such.Eucken
J
8

Are you using that same path on your device? Apps on a device are sandboxed and can only read/write files in their documents directory. Grab that file path like this and append your plist name. This approach will also work on the simulator.

Try this:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistLocation = [documentsDirectory stringByAppendingPathComponent:@"myplist.plist"];
Judson answered 17/2, 2012 at 21:20 Comment(8)
No, I haven't tried to run it on a "real" device, but i don't expect that it will run, because the directory don't exist on a "real" device. I know that they can only be read/write from their own directory, but how do i tell Xcode to use the file named data.plist in the applications directory instead of /Users/Denis/Documents/Xcode/iPhone/MLBB/data.plist ? I feel like n00b :-)Serb
That link I posted shows you how to get the documents directory as a string. Use stringByAppendingString to add your plist name on the end then use the code you specify. That's all :)Judson
Sorry, but I don't know what you mean. I've wrote NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; But i don't think that's what you mean. Can you please edit the code above so it works and when write it here. I'm new to Xcode and now i know how it feels to be a n00b :-)Serb
I've added my current code above, and cannot understand why it won't work. Hopefully I've learn to spot errors in the code, sometime :-)Serb
@DSDeniso Use setObject: forKey:, setValue: is for something different.Judson
I've pasted the new code above, it still dosen't work, how do I copy it into the resources folder? Or do I need to copy it to somewhere else?Serb
@DSDeniso You need to stop blindly pasting and start trying to understand what the code is doing.Judson
I'm really trying to understand the code, but when one says that you need to add it to a bundle I've never heard about. And the other one is saying you need to add a NSString, and then a complicated method to find out where the file is located. I will have no chance to make my application work, if i need to check every code i find it would take hundreds of years. So when i compile my application and everything works, I will go through the code and see how it works. Else I wouldn't ever learn and remember the code which is working. Can't you please just give me your code combined with mine, soSerb

© 2022 - 2024 — McMap. All rights reserved.