iosobjective-cinitwithcodernskeyedunarchiver

Issue with initWithCoder: and NSKeyedUnarchiver


I'm currently attempting to add a game-saving feature to my app. The end goal is to get ~300 custom objects saved to a .plist file and extract them again later. I've made some headway, but I'm having some issues with initWithCoder: and I'm also unsure about my technique.

The following code is being used by a UIViewController to save the objects (I only added 2 objects to the dictionary as an example):

//Saves the contents to a file using an NSMutableDictionary
-(IBAction)saveContents {

    //Create the dictionary
    NSMutableDictionary *dataToSave = [NSMutableDictionary new];

    //Add objects to the dictionary
    [dataToSave setObject:label.text forKey:@"label.text"];
    [dataToSave setObject:territory forKey:@"territory"];
    [dataToSave setObject:territory2 forKey:@"territory2"];

    //Archive the dictionary and its contents and set a BOOL to indicate if it succeeds
    BOOL success = [NSKeyedArchiver archiveRootObject:dataToSave toFile:[self gameSaveFilePath]];

    //Handle success/failure here...

    //Remove and free the dictionary
    [dataToSave removeAllObjects]; dataToSave = nil;
}

This successfully calls the encodeWithCoder: function in the Territory class twice:

-(void)encodeWithCoder:(NSCoder *)aCoder {

    NSLog(@"ENCODING");
    [aCoder encodeObject:self.data forKey:@"data"];
    [aCoder encodeInt:self.MMHValue forKey:@"MMHValue"];
    [aCoder encodeObject:self.territoryName forKey:@"territoryName"];
    //Continue with other objects
}

When I look in the directory, sure enough, the file exists. Now, here's the issue I'm having: When the following function is run, the initWithCoder: function is successfully called twice as it should be. The logs inside the initWithCoder: function output what they should, but the logs in the UIViewController's loadContents function return 0, null, etc. However, the label's text is set correctly.

//Loads the contents from a file using an NSDictionary
-(IBAction)loadContents {

    //Create a dictionary to load saved data into then load the saved data into the dictionary
    NSDictionary *savedData = [NSKeyedUnarchiver unarchiveObjectWithFile:[self gameSaveFilePath]];

    //Load objects using the dictionary's data
    label.text = [savedData objectForKey:@"label.text"];

    NSLog(@"%i, %i", [territory intAtKey:@"Key4"], [territory2 intAtKey:@"Key4"]);
    NSLog(@"MMH: %i, %i", territory.MMHValue, territory2.MMHValue);
    NSLog(@"NAME: %@, %@", territory.territoryName, territory2.territoryName);

    //Free the dictionary
    savedData = nil;
}

- (id)initWithCoder:(NSCoder *)aDecoder {

    if (self = [super initWithCoder:aDecoder]) {

        NSLog(@"DECODING TERRITORY");

        self.data = [aDecoder decodeObjectForKey:@"data"];
        self.MMHValue = [aDecoder decodeIntForKey:@"MMHValue"];
        self.territoryName = [[aDecoder decodeObjectForKey:@"territoryName"] copy];
        //Continue with other objects
    }

    NSLog(@"DECODED INT: %i", self.MMHValue);
    NSLog(@"DECODED NAME: %@", self.territoryName);
    return self;
}

I've been trying to get this to work for hours, but to no avail. If anyone has any insights to this, please help me out. Also, I'm not entirely sure if my technique for saving is good or not (using an NSMutableDictionary to store references to the objects so it can output to one file)? Thanks!


Solution

  • I read through Apple's documentation on NSKeyedArchiver and NSKeyedUnarchiver, along with a bunch of blog posts on the subject, and I realized what I was doing wrong. After encoding the objects to a file (by adding them to a dictionary using keys and then saving them to a .plist), when I tried decoding them, I never actually extracted the values from the new dictionary (the dictionary holding the contents of the file that was just decoded). This also explains why the label's text was loaded, but territory and territory2 weren't. I changed my loadContents function to the following, and now the code works:

    -(IBAction)loadContents {
    
        //Create a dictionary to load saved data into then load the saved data into the dictionary
        NSLog(@"***** STARTING DECODE *****");
        NSDictionary *savedData = [NSKeyedUnarchiver unarchiveObjectWithFile:[self gameSaveFilePath]];
        NSLog(@"***** CALLING initWithCoder: ON OBJECTS *****");
        NSLog(@"***** FINISHED DECODE *****");
    
        //Load objects using the dictionary's data
        label.text = [savedData objectForKey:@"label.text"];
        territory = [savedData objectForKey:@"territory"];
        territory2 = [savedData objectForKey:@"territory2"];
    
        //Free the dictionary
        savedData = nil;
    }