views:

1187

answers:

3

I am trying to save two strings. One string needs to be saved as type ARRAY in the pList and the second string needs to be saved as String in the Array.

I can use the code:

[dictionary setObject:(id)anObject forKey:(id)aKey>]

but it doesn't save it correctly. I can cast one of the strings as an array, but it still doesn't work right.

What is the proper method for saving an array to the pList?

Here is what my pList looks like:

<dict>
<key>Test One</key>
<array>
 <string>A13#</string>
</array>
<key>Another Test</key>
<array>
 <string>1111111111</string>
</array>
<key>Test Three</key>
<array>
 <string>2222222222</string>
</array>
<key>Final Test</key>
<array>
 <string>3333333333</string>
</array>
</dict>

here is the method I am using to try to

-(void)writeToFile:(NSString *)s1{
    NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:@"saved" ofType:@"plist"];

NSMutableDictionary *dictionary = [[[NSDictionary alloc] initWithContentsOfFile:plistPath] mutableCopy];
NSString *tempString = [NSString stringWithFormat:@"1234567"];
[dictionary setObject:tempString forKey:s1];

[dictionary writeToFile:plistPath atomically:YES];
}
+2  A: 

You can't cast or otherwise convert a string into an array; they're separate, distinct objects. It's the same as if in real life you try to turn your dog into a station wagon, it isn't happening.

Instead, put your dog inside the station wagon (or put your string(s) inside an array). You can create the array with [NSArray arrayWithObjects:@"string1", @"string2", nil];. Stick that inside your dictionary for a given key, along with your final string for another key, save it, and you'll have a plist with an array of one or more strings.

Also, in your code example your dictionary is leaking memory. Read up on memory management in Objective-C, you're going to run into lots of crashes and performance issues until you understand it well.

Marc Charbonneau
In fact, you can't cast ANY object from one class to another.
Chuck
Not true, you can if the object you're casting can actually behave as the class it's being casted to. For example, say you have an NSMutableArray object stored in an `NSArray* array` pointer. If you try to call `-removeLastObject` on it, the compiler will use static typing and inform you that an NSArray won't respond to that message. However, you can do `[(NSMutableArray*)array removeLastObject] just fine. You'll run into problems if `array` is actually an NSArray, or if you cast an object stored in an `id` variable to an incompatible class, such as strings to arrays.
Quinn Taylor
A: 

You an convert a string to a single element array with

[NSArray arrayWithObject:str];

So if you want your plist to contain entries as arrays of strings, and you want just a single string as an element, then you do something like:

[dictionary setObject:[NSArray arrayWithObject:tempString] forKey:s1];

I don't actually no why you would want it this way unless you want to allow for multiple strings per key at some other time.

Also, as Marc mentioned, you are leaking the initial (unmutable) dectionary you create. Read the memory management rules at http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html.

Further, you should never write inside your application's bundle. For one thing, your application may be on a write protected volume or the current user may not have permissions to change it. For another, the file would then be shared by all users. And for a third, it would break the code signing. Instead, write the file to either the Preferences folder or the Application Support folder.

And finally, if these are intended to be user preferences of some sort, then you should use the preferences system, which allows configuring defaults and stores the preferences in the preferences folder for you. See http://developer.apple.com/documentation/Cocoa/Conceptual/UserDefaults/UserDefaults.html for more information.

Peter N Lewis
A: 

The correct way to save an NSArray (by itself) to a plist file is as follows:

NSArray* anArray = ...;
[anArray writeToFile:@"/path/to/file.plist" atomically:YES];

However, you can't save an NSString as an array. Given the XML plist you provided, if you want to add entries with the same format, you can use this much simpler code:

- (void) writeToFile:(NSString *)string {
  NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"saved" ofType:@"plist"];
  NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
  [dictionary setObject:[NSArray arrayWithObject:@"1234567"] forKey:string];
  [dictionary writeToFile:plistPath atomically:YES];
}

This also avoids a memory leak in your code, where the receiver of -mutableCopy escapes with a retain count of 1. (This isn't a problem under GC, but it's still bad practice.) You shouldn't need to use +[NSString stringWithFormat:], just use a string literal. If you want to use a different as the string in the array, you can either pass it in as an additional parameter, grab it from another method, etc.

This method is still brittle in that it only stores one string in the array matched with the given key — also, the method name would be better if it were more indicative of exactly what it does. Also, if there will only ever be one string value for each key, you might consider revising the plist to omit the arrays entirely, since it just chews up space and complicates the code.

Quinn Taylor
Note that in this case, although +dictionaryWithContentsOfFile is a convenience constructor on NSDictionary, it is inherited by NSMutableDictionary, and will return an instance of the receiver class. This works for other mutable/immutable classes in Foundation, such as NS(Mutable)String, NS(Mutable)Array, etc.
Quinn Taylor