Property Lists, or plists, are collections of strings, numbers, booleans, dates, base64 data, arrays, and dictionaries (associative arrays). They are a standardized, flexible data structure used (among other places) throughout Mac OS X. Plists are stored as XML files on disk, and Cocoa provides several methods to load them into memory. Unfortunately, the most common methods return immutable plists, which can be inconvenient to convert to mutable data structures. Today, I want to present a quick-and-dirty way to way to load plists as mutable structures.
Example
Let’s say you have the following plist on disk:
<?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>alpha</key>
<array>
<integer>0</integer>
<integer>1</integer>
<integer>2</integer>
</array>
<key>bravo</key>
<array>
<integer>3</integer>
<integer>4</integer>
<integer>5</integer>
</array>
<key>charlie</key>
<array>
<integer>6</integer>
<integer>7</integer>
<integer>8</integer>
</array>
</dict>
</plist>
Cocoa
You can load this plist with the following code:
// Assume that path is the pathname of a file with the XML contents shown above
NSDictionary* elements = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
However, the resulting object is not mutable. You could try this:
NSMutableDictionary* m_elements = [elements mutableCopy];
but mutableCopy
only yields a shallow copy. (I.e. the top-level dictionary will be mutable, but the arrays it contains will be immutable.)
(Fun fact: The iPhone simulator doesn’t seem to distinguish between mutable and immutable data structures – for instance, if you send a replaceObjectAtIndex:withObject:
message to an NSArray, it will be processed just fine. Be wary of this when developing with the simulator.)
Core Foundation
Fortunately, Core Foundation offers a lower-level function that will create a collection of mutable data structures. A fuller discussion is given here, but this is the short version:
// Assume that path is the pathname of a file with the XML contents shown above
CFStringRef errStr;
NSMutableDictionary* elements = (NSMutableDictionary*) CFPropertyListCreateFromXMLData(kCFAllocatorDefault, (CFDataRef) [NSData dataWithContentsOfFile:path], kCFPropertyListMutableContainersAndLeaves, &errStr);