views:

446

answers:

4

edited.

Hey, I am trying to write an NSMutableArray to a plist. The compiler does not show any errors, but it does not write to the plist anyway. I have tried this on a real device too, not just the Simulator.

Basically, what this code does, is that when you click the accessoryView of a UITableViewCell, it gets the indexPath pressed, edits an NSMutableArray and tries to write that NSMutableArray to a plist. It then reloads the arrays mentioned (from multiple plists) and reloads the data in a UITableView from the arrays.

Code:

NSIndexPath *indexPath = [table indexPathForRowAtPoint:[[[event touchesForView:sender] anyObject] locationInView:table]];

[arrayFav removeObjectAtIndex:[arrayFav indexOfObject:[NSNumber numberWithInt:[[arraySub objectAtIndex:indexPath.row] intValue]]]];

NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:@"arrayFav.plist"];
NSLog(@"%@ - %@", rootPath, plistPath); 


[arrayFav writeToFile:plistPath atomically:YES];    

// Reloads data into the arrays
[self loadDataFromPlists];

// Reloads data in tableView from arrays
[tableFarts reloadData];

CFShow() on the array after removing one of them shows this:

<CFArray 0x6262110 [0x2c810a0]>{type = mutable-small, count = 4, values = (
    0 : <CFNumber 0x6502e10 [0x2c810a0]>{value = +3, type = kCFNumberSInt32Type}
    1 : <CFNumber 0x6239de0 [0x2c810a0]>{value = +8, type = kCFNumberSInt32Type}
    2 : <CFNumber 0x6239dc0 [0x2c810a0]>{value = +10, type = kCFNumberSInt32Type}
    3 : <CFNumber 0x6261420 [0x2c810a0]>{value = +40, type = kCFNumberSInt64Type}

DEBUG-INFO: writeToPlist shows YES, I have tried to release all the arrays before filling them up again, setting them to nil, set atomically to NO.

+3  A: 
[yourMutableArray writeToFile:fileName atomically:YES];

This should work. NSMutableArray inherits from NSArray which has a method to write to a plist.

Deepak
Thanks, that should probably do it, but it does not seem to work. See updated post.
Emil
That should work. Check your array contents to make sure that you have only NSString, NSData, NSArray or NSDictionary objects in it. Check the BOOL result returned by writeToFile. Read the description of writeToFile for more details.
progrmr
The array actually contains `NSNumber`-s. Would that have anything to do with it?
Emil
The BOOL returns 1 (YES).
Emil
A: 

Yes

Look at the Property List Programming Guide.

phoneNumbers is a NSMutableArray

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
    NSString *error;
    NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
    NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
            [NSArray arrayWithObjects: personName, phoneNumbers, nil]
            forKeys:[NSArray arrayWithObjects: @"Name", @"Phones", nil]];
    NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
                            format:NSPropertyListXMLFormat_v1_0
                            errorDescription:&error];
    if(plistData) {
        [plistData writeToFile:plistPath atomically:YES];
    }
    else {
        NSLog(error);
        [error release];
    }
    return NSTerminateNow;
}
willcodejavaforfood
That uses an NSDictionary..
Emil
which will be written to a plist...
willcodejavaforfood
Yeah, but how do I make a NSDictionay out of ONE NSMutableArray?
Emil
Just follow the example
willcodejavaforfood
+1  A: 

writeToFile:atomically: won't work if your array contains custom objects.

If your array contains custom objects that are not Plist objects (NSArray, NSDictionary, NSString, NSNumber, etc), then you will not be able to use this method. This method only works on Plist objects.

Another option would be to use the NSCoding protocol, and write your objects to disk that way.

Jasarien
The array contains `NSNumber`-s.
Emil
Edit: according to CFShow it actually contains CFNumbers.
Emil
It's not a problem then.
Jasarien
+2  A: 

As discussed in the comments below, the actual problem here is that the plist is being read from and written to two different locations. Somewhere in the app, there is code that reads the file into the array similar to this:

NSString *plistFavPath = [[NSBundle mainBundle] pathForResource:@"arrayFav" 
                                                         ofType:@"plist"]; 
arrayFav = [[NSMutableArray alloc] initWithContentsOfFile:plistFavPath];

This logic reads the array from the application's bundle, which is a read-only location and part of the distributed app. Later when the edited array is persisted, code similar to this is used:

NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                          NSUserDomainMask,
                                                          YES) objectAtIndex:0];
NSString *plistPath = [rootPath 
                          stringByAppendingPathComponent:@"arrayFav.plist"];
NSLog(@"%@ - %@", rootPath, plistPath); 
[arrayFav writeToFile:plistPath atomically:YES];   

The result here is that the updated file gets written to the app's documents directory, but it is never read from there, giving the appearance that the file is not being saved correctly. To correct this, you should change the code that reads the file to use the same path that you are writing to.

If you need to distribute a default version of the plist for use on the initial launch before the array has been edited, you could continue to include a version of the file in your bundle and then add code to your app delegate that check if the file exists in the documents directory and if it is not present, copies the bundle's default version of the file to the proper place.

Jason Jenkins
What..? No? I am getting the path with this method: `NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *plistPath = [rootPath stringByAppendingPathComponent:@"arrayFav.plist"];`
Emil
In that case then, it seems to me that what you may be doing is reading the plist from one place (your applications bundle) and writing it to another (your applications Document directory). Thus you would have two copies of the plist. When I asked in my comment to the original question above how you were creating the array to begin with, the code you gave indicated that you are reading the file from the bundle, so I assumed you were trying to write the updated file to the same place. After writing the file to the Documents directory, when you reload it, how are you getting the path?
Jason Jenkins
In other words, where are you doing this: NSString *plistFavPath = [[NSBundle mainBundle] pathForResource:@"arrayFav" ofType:@"plist"]; arrayFav = [[NSMutableArray alloc] initWithContentsOfFile:plistFavPath];Is that only the first time the file is read, or does it happen on every launch, resulting in the changes you saved to the docs directory being ignored?
Jason Jenkins
Jason, you are right, I am reading and writing to two different places. If you edit your answer, I'll choose it as the correct one.
Emil
Done. Glad to be of help.
Jason Jenkins
Thank you, I'll be able to fix it now ! :)
Emil