views:

642

answers:

2

I'm trying to serialize two NSMutableArrays of NSObjects that implement the NSCoding protocol. However it works for one (stacks) and not the other (cards). I have the following block of code:

-(void) saveCards
{
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString* documentsDirectory = [paths objectAtIndex:0];

    NSString* cardsFile = [documentsDirectory stringByAppendingPathComponent:@"cards.state"];
    NSString* stacksFile = [documentsDirectory stringByAppendingPathComponent:@"stacks.state"];
    BOOL c = [rootStack.cards writeToFile:cardsFile atomically:YES];
    BOOL s = [rootStack.stacks writeToFile:stacksFile atomically:YES];
}

I step through this method using the debugger, and after the last two lines of code run, I check the values of the two BOOLs. BOOL c is NO and BOOL s is YES. The stacks array is actually empty (which is probably why it works). The cards array has contents. Why is it that the array with contents is failing? I can't figure this out. I've looked through numerous threads on SOF, each of them say the problem is because the protection level of the files they were writing were preventing them from writing. This is not my problem, as I'm writing to the Documents folder. I've double and tripple checked that neither rootStack.cards nor rootStack.stacks is nil. And I've checked that cards does indeed have content.

Here are the coder methods for my Notecard class (I added all the if statments as part of trying to solve this problem to make sure trying to encode nil values doesn't break something):

-(void) encodeWithCoder:(NSCoder *)encoder
{
    if(text)
        [encoder encodeObject:text forKey:@"text"];
    if(backText)
        [encoder encodeObject:backText forKey:@"backText"];
    if(x)
        [encoder encodeObject:x forKey:@"x"];
    if(y)
        [encoder encodeObject:y forKey:@"y"];
    if(width)
        [encoder encodeObject:width forKey:@"width"];
    if(height)
        [encoder encodeObject:height forKey:@"height"];
    if(timeCreated)
        [encoder encodeObject:timeCreated forKey:@"timeCreated"];
    if(audioManagerTicket)
        [encoder encodeObject:audioManagerTicket forKey:@"audioManagerTicket"];
    if(backgroundColor)
        [encoder encodeObject:backgroundColor forKey:@"backgroundColor"];
}

-(id) initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if(!self)
        return nil;

    self.text               = [decoder decodeObjectForKey:@"text"];
    self.backText           = [decoder decodeObjectForKey:@"backText"];
    self.x                  = [decoder decodeObjectForKey:@"x"];
    self.y                  = [decoder decodeObjectForKey:@"y"];
    self.width              = [decoder decodeObjectForKey:@"width"];
    self.height             = [decoder decodeObjectForKey:@"height"];
    self.timeCreated        = [decoder decodeObjectForKey:@"timeCreated"];
    self.audioManagerTicket = [decoder decodeObjectForKey:@"audioManagerTicket"];
    self.backgroundColor    = [decoder decodeObjectForKey:@"backgroundColor"];

    return self;
}

each field is either an NSString, NSNumber, or UIColor.

Thanks for any help

+8  A: 

You are confusing property lists with archiving. It is easy to do.

writeToFile:atomically: requires that the contents be property list compliant; that is, all objects in the array be an instance of one of the classes allowed in property lists (dictionaries, strings, data, arrays, etc...).

See: writeToFile:atomically:'s documentation.

What you want is NSArchiver and, more specifically, to read the archiving guide.

bbum
Perfect, thanks
Joel
+3  A: 

The writeToFile:atomically: method on NSMutableArray you are calling only works if all of the elements of the array are property list objects (NSString, NSData, NSArray, or NSDictionary objects.) It is doing NSPropertyListSerialization. This is not the same as the keyed archiving that is done using NSKeyedArchiver and the NSCoding protocol. See the Archives and Serialization Guide for Cocoa for details.

The specific problem here is that UIColor is not a property list object. It does however implement the NSCoding protocol and should work in a archive. Try something like

BOOL c  = [NSKeyedArchiver archiveRootObject:rootStack.card
                                      toFile:cardsFile];
BOOL s  = [NSKeyedArchiver archiveRootObject:rootStack.stacks
                                      toFile:stacksFile];
Jason Jenkins