I'm writing some code for an iPhone app, and I'm having issues getting default data to load in correctly. I am basing my code off some example from the "Learning Cocos2d" book by Ray Wenderlich.
It seems that even when I delete the app outright and try to start from fresh data that the app inconsistently either doesn't try to load the data, or incorrectly thinks that there is data, and loads null.
I'm using containsValueForKey to check if a value exists and then load it or load some default value, but even on a fresh installation the containsValueForKey finds data and doesn't load the defaults. In xcode's organizer I checked my device's file structure and the Documents folder, where I specified to save, doesn't look like it contains any files, so I'm not sure what it's grabbing.
My guess is that the problem is something to do with the initWithCoder function. It seems to mysteriously go through the function sometimes, but not all the time. Another weird thing is that I call [[GameManager sharedGameManager] save]
when the player gets a highscore (not shown here, but the code is the exact same as this objectiveList, only an int) and it appears to save it correctly.
And now the code:
GCDatabase.h
#import <Foundation/Foundation.h>
id loadData(NSString * filename);
void saveData(id theData, NSString *filename);
GCDatabase.m
#import "GCDatabase.h"
NSString * pathForFile(NSString *filename) {
// 1
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
// 2
NSString *documentsDirectory = [paths objectAtIndex:0];
// 3
return [documentsDirectory stringByAppendingPathComponent:filename];
}
id loadData(NSString * filename) {
NSString *filePath = pathForFile(filename);
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *data = [[[NSData alloc] initWithContentsOfFile:filePath] autorelease];
NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
id retval = [unarchiver decodeObjectForKey:@"Data"];
[unarchiver finishDecoding];
return retval;
}
return nil;
}
void saveData(id theData, NSString *filename) {
NSMutableData *data = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
[archiver encodeObject:theData forKey:@"Data"];
[archiver finishEncoding];
[data writeToFile:pathForFile(filename) atomically:YES];
}
GameManager.h
@interface GameManager : NSObject <NSCoding>{
NSMutableArray *objectiveDescriptions;
}
@property (nonatomic, retain) NSMutableArray * objectiveDescriptions;
+(GameManager*)sharedGameManager;
-(void)save;
-(void)load;
-(void)encodeWithCoder:(NSCoder *)encoder;
-(id)initWithCoder:(NSCoder *)decoder;
@end
GameManager.m (I added the load function, in an attempt to force it to load, but it doesn't seem to work)
+(GameManager*)sharedGameManager {
@synchronized([GameManager class])
{
if(!sharedGameManager) {
sharedGameManager = [loadData(@"GameManager") retain];
if (!sharedGameManager) {
[[self alloc] init];
}
}
return sharedGameManager;
}
return nil;
}
+(id)alloc {
@synchronized([GameManager class]){
NSAssert(sharedGameManager == nil, @"Attempted to allocate a second instance of the Game Manager singleton");
sharedGameManager = [super alloc];
return sharedGameManager;
}
return nil;
}
- (void)dealloc {
[objectiveList release];
[super dealloc];
}
- (void)save {
saveData(self, @"GameManager");
}
-(void)load {
loadData(@"GameManager");
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:objectiveList forKey:@"objectiveList"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
if ([decoder containsValueForKey:@"objectiveList"]) {
objectiveList = [decoder decodeObjectForKey:@"objectiveList"];
} else {
[objectiveList addObjectsFromArray:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]];
}
}
return self;
}
@end