When working on the review downloading for MyAppSales I found a situation where I needed to convert NSData I received from an NSURLConnection directly into the NSDictionary which it represents. Neither class really helps you there, there are only two methods to instantiate an NSDictionary from external contents:
- dictionaryWithContentsOfFile
- dictionaryWithContentsOfURL
Now where is the missing dictionaryWithContentsOfData? Well, it turns out that such a method exist, but it’s way down the frameworks stack burried in Core Foundation. Let’s make a useful category extension for NSData to fix this for our purposes.
NSDictionary+Helpers.h
@interface NSDictionary (Helpers) + (NSDictionary *)dictionaryWithContentsOfData:(NSData *)data; @end |
NSDictionary+Helpers.m
#import "NSDictionary+Helpers.h" @implementation NSDictionary (Helpers) + (NSDictionary *)dictionaryWithContentsOfData:(NSData *)data { // uses toll-free bridging for data into CFDataRef and CFPropertyList into NSDictionary CFPropertyListRef plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, (CFDataRef)data, kCFPropertyListImmutable, NULL); // we check if it is the correct type and only return it if it is if ([(id)plist isKindOfClass:[NSDictionary class]]) { return [(NSDictionary *)plist autorelease]; } else { // clean up ref CFRelease(plist); return nil; } } @end |
Instead of using kCFPropertyListImmutable as mutability option you could also have used any other option to make all or parts of the dictionary mutable. Available options are, their names are almost self-explanatory, containers are NSArray and NSDictionary, leaves are types like NSString or NSNumber.
- kCFPropertyListImmutable
- kCFPropertyListMutableContainers
- kCFPropertyListMutableContainersAndLeaves
Of course you can always copy/past such CF code into your apps, but creating a category extension with the proper name makes your live much easier because you can collect these and reuse them in all your future projects.
Categories: Administrative
I suggest your method name should be NSDictionaryWithXMLData to be consistent with the CF counterpart.
If the provided NSData is not a dictionary, the method throws an exception at CFRelease(plist). You need to test that plist is not nil before releasing:
if( plist != nil ) { CFRelease(plist); }