Skip to content

Instantly share code, notes, and snippets.

@dhoerl
Created January 27, 2012 13:10
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhoerl/1688697 to your computer and use it in GitHub Desktop.
Save dhoerl/1688697 to your computer and use it in GitHub Desktop.
Converting a NSDictionary to a Binary PList
When I posted this on Apple’s Cocoadev listserver, I never did get pointers to code to convert a NSDictionary to a binary formatted plist, but did get pointers to look at NSPropertyListSerialization - something I had already (mis)read a few times.
The description for the dataFromPropertyList class method seems confusing if you don't understand that you can create a property list with no keys: that is, you can create one with a single compliant object, and the key becomes "root".
So, in the unlikely event that some else someday has the same mental block, here is actual tested code that takes a NSDictionary and creates a binary plist file:
NSDictionary *dict;
char *payload = "This is the payload";
dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"Hello world", @"greeting",
[NSData dataWithBytes:payload length:strlen(payload)], @"payload",
[NSNumber numberWithInt:10], @"result",
nil ];
// get a text representation, for comparison
[dict writeToFile:@"/tmp/text.plist" atomically:NO];
NSString *err = nil;
NSData *plist;
NSFileManager *manager;
BOOL ret;
manager = [NSFileManager defaultManager];
plist = [NSPropertyListSerialization dataFromPropertyList:dict
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&err];
if(plist == nil) {
NSLog(@"NSPropertyListSerialization error: %@", err);
return -1;
}
ret = [manager createFileAtPath:@"/tmp/binary.plist" contents:plist attributes:nil];
if(ret == NO) {
NSLog(@"Create failed");
return -1;
}
Just to prove to myself that yes, you can create a property list with other objects like NSNumbers or raw NSData, I added these two tests:
plist = [NSPropertyListSerialization dataFromPropertyList:[NSNumber numberWithInt:1]
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&err];
if(plist == nil) {
NSLog(@"NSPropertyListSerialization error: %@", err);
return -1;
}
ret = [manager createFileAtPath:@"/tmp/tst1.plist" contents:plist attributes:nil];
if(ret == NO) {
NSLog(@"Create failed");
return -1;
}
plist = [NSPropertyListSerialization dataFromPropertyList:[NSData dataWithBytes:payload length:strlen(payload)]
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&err];
if(plist == nil) {
NSLog(@"NSPropertyListSerialization error: %@", err);
return -1;
}
ret = [manager createFileAtPath:@"/tmp/tst2.plist" contents:plist attributes:nil];
if(ret == NO) {
NSLog(@"Create failed");
return -1;
}
Then, Ken Thomases (on Cocoadev) provided a shorter version, by having the NSData instance write the file. Jean-Daniel Dupas suggested using CFPropertyListWriteToStream to bypass the intermediate NSData object (a speed and memory footprint improvement). Here is the above code wrapped in a short command line test program:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *err = nil;
NSData *plist;
NSDictionary *dict;
char *payload = "This is the payload";
dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"Hello world", @"greeting",
[NSData dataWithBytes:payload length:strlen(payload)], @"payload",
[NSNumber numberWithInt:10], @"result",
nil ];
// 1 - get a XML (text) file representation
[dict writeToFile:@"/tmp/text.plist" atomically:NO];
// get a binary file representation - creates an additional NSData in memory object
plist = [NSPropertyListSerialization dataFromPropertyList:dict
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&err];
if(plist == nil) {
NSLog(@"NSPropertyListSerialization error: %@", err);
return -1;
}
// 2 - have the NSData object save a binary representation
[plist writeToFile:@"/tmp/binary1.plist" atomically:NO];
// get a binary file representation - no additional memory footprint
// NOTE: documentation does not state it, but NSOutputStream creates the file if it does not yet exist
NSOutputStream *str = [NSOutputStream outputStreamToFileAtPath:@"/tmp/binary2.plist" append:NO];
if(str == nil) {
NSLog(@"cannot create output stream");
return -1;
}
// 3 - stream a binary representation
[str open];
CFIndex idx = CFPropertyListWriteToStream(dict, (CFWriteStreamRef)str, kCFPropertyListBinaryFormat_v1_0, (CFStringRef *)&err);
if(idx == 0) {
NSLog(@"CFPropertyListWriteToStream error: %x %@", err, err);
return -1;
}
[str close];
[pool drain];
return 0;
}
Hope this is of use to someone else.
@dhoerl
Copy link
Author

dhoerl commented Jan 27, 2012

Originally posted in 2008 on my iWeb site (iWeb is going away soon).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment