Reputation: 945
I have written the following method to encode/decode data..
- (void) encode: (BOOL) encodeBool int: (NSNumber *) integer boolean:(BOOL) boolean key: (NSString *) keyStr {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *gameStatePath = [documentsDirectory stringByAppendingPathComponent:@"gameData"];
if (encodeBool == YES) {
NSMutableData *gameData = [NSMutableData data];
NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:gameData];
if (integer) {
[encoder encodeInt:[integer intValue] forKey:keyStr];
}
else if (boolean) {
[encoder encodeBool:boolean forKey:keyStr];
}
[encoder finishEncoding];
[gameData writeToFile:gameStatePath atomically:YES];
[encoder release];
} else {
NSMutableData *gameData = [NSData dataWithContentsOfFile:gameStatePath];
if (gameData) {
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:gameData];
if (integer) {
NSLog(@"%d", [decoder decodeIntForKey:keyStr]);
}
else if (boolean) {
if ([decoder decodeBoolForKey:keyStr]==YES) {
NSLog(@"YES");
} else {
NSLog(@"NO");
}
}
[decoder finishDecoding];
[decoder release];
}
}
}
And some testing
[[GameData sharedData] encode:YES int: [NSNumber numberWithInt:100] boolean:NO key:@"testInt"];
[[GameData sharedData] encode:YES int:nil boolean:YES key:@"bool"];
[[GameData sharedData] encode:YES int:[NSNumber numberWithInt:1030] boolean:nil key:@"test"];
[[GameData sharedData] encode:NO int: [NSNumber numberWithInt:1] boolean:nil key:@"testInt"];
[[GameData sharedData] encode:NO int:nil boolean:YES key:@"bool"];
[[GameData sharedData] encode:NO int:[NSNumber numberWithInt:100] boolean:nil key:@"test"];
and output is
0
NO
1030
only the last one is correct.. Can someone tell me what I am doing wrong? Thanks
Upvotes: 0
Views: 2281
Reputation: 18363
Your problem is that every time you call your method, you overwrite the file - erasing the values you encoded in previous calls. You should probably rewrite your method so that you encode all the values in a single call.
One alternative is to create a GameState
object and have it implement NSCoding
, then read and serialize it with +[NSKeyedArchiver archiveRootObject:toFile:]
and deserialize it with +[NSKeyedUnarchiver unarchiveObjectWithFile:]
. The code to do so looks a bit like this:
@interface GameState : NSObject <NSCoding>
@property (nonatomic) int someInt;
@property (nonatomic) BOOL someBool;
@property (nonatomic, strong) NSString *someString;
@end
static NSString *const BoolKey = @"BoolKey";
static NSString *const StringKey = @"StringKey";
static NSString *const IntKey = @"IntKey";
@implementation GameState
- (id)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
_someBool = [coder decodeBoolForKey:BoolKey];
_someInt = [coder decodeIntForKey:IntKey];
_someString = [coder decodeObjectForKey:StringKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeBool:self.someBool forKey:BoolKey];
[aCoder encodeInt:self.someInt forKey:IntKey];
[aCoder encodeObject:self.someString forKey:StringKey];
}
@end
// Somewhere in your app where reading and saving game state is needed...
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = nil;
if ([paths count]) {
documentsDirectory = paths[0];
}
NSString *archivePath = [documentsDirectory stringByAppendingPathComponent:@"archive"];
GameState *gameState = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
if (!gameState) {
gameState = [[GameState alloc] init];
gameState.someString = @"a string";
gameState.someInt = 42;
gameState.someBool = YES;
}
// Make changes to gameState here...
[NSKeyedArchiver archiveRootObject:gameState toFile:archivePath];
Upvotes: 2
Reputation: 66242
The first problem is when you test if (boolean)
it's the same as saying if (boolean == YES)
. Bools aren't objects, and can't be nil
. When you pass nil
in as a bool, it's the same as passing NO
in. I don't think this accounts for all of your issues though. I think the file is not saving as well.
From the NSKeyedUnarchiver docs:
If you invoke one of the decode... methods of this class using a key that does not exist in the archive, a non-positive value is returned. This value varies by decoded type. For example, if a key does not exist in an archive, decodeBoolForKey: returns NO, decodeIntForKey: returns 0, and decodeObjectForKey: returns nil.
These are the erroneous values you're getting. To start, I note that you're not doing any error checking. Try adding some checks to see what's failing, for example, you could try:
[encoder finishEncoding];
NSError *error;
BOOL success = [gameData writeToFile:gameStatePath options:NSDataWritingAtomic error:&error];
if (success == NO) NSLog(@"Error: %@", [error localizedDescription]);
Once you get an error, we can go from there.
Upvotes: 2