“NSPropertyListWriteStreamError” – When No is Not Enough

By: Stephen Furlani 07/25/2011

Sometimes, ‘NO’ is just not enough.  There are many great features about XCode, the LLVM compiler, Objective-C and the Cocoa Touch libraries but sometimes they just don’t give enough information.  One such case is NSDictionary’s “-(BOOL)writeToURL:atomically:” method.

I was working on a project, and I just wanted to store some information to the documents directory.

[cpp autolinks="false" classname="NSDictionary" firstline="1" gutter="true" tabsize="4" collapse="true"] NSDictionary *allData = [NSDictionary dictionaryWithObjectsAndKeys: imageArray, @"images", contactArray, @"contacts", profilesDict, @"profiles", nil]; [allData writeToURL:filename atomically:YES]; [/cpp]

and of course, writeToURL:, fails with a ‘NO’ value.

I scoured my data for invalid objects, or things that wouldn’t save correctly (i.e. not a valid plist value), but no luck.  All my data looked correct and of course there’s no error code, NSError, or NSException associated with this, so I switched to the more fancy writer hoping to get something more clear, but NSPropertyListSerialization just gave me “Error Code: 3851″ which is “NSPropertyListWriteStreamError“.

Not much more descriptive than ‘NO’…

So I wrote a simple category with a recursive method for NSObject that traverses the object graph and tells me what objects at runtime are compatible or not with a given NSPropertyListFormat (requires iOS 4 and Blocks). Pass ‘nil’ in the first time for ‘path’.

[cpp autolinks="false" firstline="1" gutter="true" tabsize="4" collapse="true"]
@implementation NSObject (DiscoverPLIST)
- (void) discoverPlist:(NSPropertyListFormat)format andPath:(NSString *)path
{
if (![NSPropertyListSerialization propertyList:self isValidForFormat:format]) {
NSLog(@"%@ – not valid for format: %d", path, format);
}
if ([self isEqual:[NSNull null]]) {
NSLog(@"%@ = (NSNull*)%@", path, self);
} else if ([self isKindOfClass:[NSArray class]]) {
[(NSArray*)self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *newPath = [path stringByAppendingFormat:@"[%i]",idx];
[obj discoverPlist:format andPath:newPath];
}];
} else if ([self isKindOfClass:[NSDictionary class]]) {
[(NSDictionary*)self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSString *newPath = [path stringByAppendingFormat:@".%@",key];
[obj discoverPlist:format andPath:newPath];
}];
} else if ([self isKindOfClass:[NSString class]]) {
NSLog(@"%@ = (NSString*)%@", path, self);
} else if ([self isKindOfClass:[NSNumber class]]) {
NSLog(@"%@ = (NSNumber*)%@", path, self);
} else if ([self isKindOfClass:[NSDate class]]) {
NSLog(@"%@ = (NSDate*)%@", path, self);
} else if ([self isKindOfClass:[NSData class]]) {
NSLog(@"%@ = (NSData*)%@", path, [self description]);
} else {
NSLog(@"%@ = Invalid Class: %@", path, [self class]);
}
}
@end
[/cpp]

And that told me the data objects in my tree where all correct!  Except the root and “profilesDict” object – which confused me.  So, I took things out of that dictionary and and put them into an array, and voila!  Saved correctly.

So what was wrong?  I was using NSNumber as a Key for values in “profilesDict” (customer IDs or something similar)  Lesson Learned:  Only NSStrings are valid Keys for saving to a property list, even though you can use NSNumber and even other custom objects as keys for NSDictionaries.

Leave a Reply




Developing Mobile Apps for iOS, Android, Windows banner graphic

How can I be sure that I own the intellectual property and source code of my app?

I have a great idea for a mobile application, but I know nothing about designing and developing a mobile app and I want to make...

More questions and answers in The Advice Section

What Our Clients Are Saying

Our confidence in Accella allowed us to introduce them to other divisions in our company, and also one of our most important customers, all of which have now completed multiple development projects.

We considered off-shore developers, briefly, and our company has also considered other providers as new projects arise, but we have always returned to Accella, as we value the consistency in work product, and most of all the ease of working with their staff.

Tim McGough NRP
Zoll Medical

Get a Quote Contact Us