3

I am storing some dictionaries in plist files (iOS), which are subsequently encrypted. After reading the file contents and decrypting them I am returning the xml contents of the file in a string:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>password</key>
    <string>apassword</string>
    <key>username</key>
    <string>ausername</string>
</dict>
</plist>

I am aware of the methods: dictionaryWithContentsOfFile:(NSString *) and dictionaryWithContentsOfFile:(NSURL *) to create a dictionary from this type of data but am surprised there is no such dictionaryWithXML:(NSString *)

Short of writing this data to a file then reading it, something I was trying to avoid as it is just excessive, are there any obvious workarounds I'm missing?

uchuugaka
  • 12,679
  • 6
  • 37
  • 55
amcc
  • 2,608
  • 1
  • 20
  • 28
  • possible duplicate of [Parse Plist (NSString) into NSDictionary](http://stackoverflow.com/questions/1072308/parse-plist-nsstring-into-nsdictionary) – ismail Feb 28 '13 at 16:32

2 Answers2

7

NSPropertyListSerialization has a handy method that does it for you, reading from an NSData instance:

NSString *source = ... // The XML string

NSData* plistData = [source dataUsingEncoding:NSUTF8StringEncoding];

NSError *error;

NSMutableDictionary* plist = [NSPropertyListSerialization propertyListWithData:plistData
                                                                       options: NSPropertyListImmutable
                                                                        format:NULL
                                                                         error:&error];

As pointed out by @Adam in the comments, the dictionary returned by this method is always mutable. The options parameter serves to determine if the containers (arrays, dictionaries) held within the plist are also mutable or (the default) immutable.

If you want containers in the property list to also be mutable, you can use NSPropertyListMutableContainers - or NSPropertyListMutableContainersAndLeaves, if you need even the leaves to be mutable.

Monolo
  • 18,205
  • 17
  • 69
  • 103
  • 1
    The `propertyListWithData:options:format:error:` method wil always return a `NSMutableDictionary` instance. The `options` parameter affects its contents. – Adam Feb 22 '13 at 12:25
  • @Adam I just tested it with `NSPropertyListImmutable` and got a `__NSCFDictionary`, which I believe is immutable, so maybe we are not talking about the same thing? – Monolo Feb 22 '13 at 13:12
  • @Monolo `__NSCFDictionary` is a private class of a class cluster. `NSMutableDictionary` can be an instance of `__NSCFDictionary` or `__NSDictionaryM` or any other class Apple will decide to use under the hood. AFAIK, `__NSCFDictionary` can be either mutable or immutable. To check for mutability I would do this: `[dict isKindOfClass:[NSMutableDictionary class]]`. – Adam Feb 22 '13 at 13:52
2

As @Monolo stated in his answer, NSPropertyListSerialization is the way to go. However, you can get a NSMutableDictionary without copying the data to a new instance. Here is the code:

NSString *str = @"<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plist version=\"1.0\"><dict><key>password</key><string>apassword</string><key>username</key><string>ausername</string></dict></plist>";
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSPropertyListFormat format;
NSMutableDictionary *dict = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:&format error:&err];
if (err) {
    NSLog(@"err: %@", err);
}
NSLog(@"original dictionary: %@", dict);
[dict setValue:@"newPass" forKey:@"password"];
NSLog(@"modified dictionary: %@", dict);
Adam
  • 26,549
  • 8
  • 62
  • 79