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!
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;
}