Question

I have a bunch of misc data related to levels and such that I need to save, that will be saved even if the player turns their phone off / on, restarts the device, exits the game, etc. Basically persistent data. I have looked at a lot of my options but have not found a simple, clear method for what I need and would like someone to please help me out and give a clear example of how to implement the basis of the best method for my needs.

I have looked at the following NSUSerDefaults (Obviously not the best, as it's for preferences, so I understand) NSCoder / NSKeyedArchiver (Can't figure out a clear way to just save simple data type from 1 single class that all the data is saved in as properties) SQLite3 (Completely Lost)

Any help and direction would be greatly appreciated.

The data types I need to save and easily access throughout my program are... NSStrings, NSArrays, Ints, Bools.

Thank you for the help and I hope to get a clear answer!

Was it helpful?

Solution

There's certainly nothing wrong with saving to NSUserDefaults, but if you want to save your properties to disk, I've put together some code for you for saving to a .plist file and then later retrieving it. You can also find it in this gist.

Saving

// We're going to save the data to SavedState.plist in our app's documents directory
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:@"SavedState.plist"];

// Create a dictionary to store all your data
NSMutableDictionary *dataToSave = [NSMutableDictionary dictionary];

// Store any NSData, NSString, NSArray, NSDictionary, NSDate, and NSNumber directly.  See "NSPropertyListSerialization Class Reference" for more information.
NSString *myString = @"Hello!"
[dataToSave setObject:myString forKey:@"MyString"];

// Wrap primitives in NSValue or NSNumber objects.  Here are some examples:
BOOL someBool = YES;
NSNumber *boolValue = [NSNumber numberWithBool:someBool];
[dataToSave setObject:boolValue forKey:@"SomeBoolValue"];
int someInteger = 99;
NSInteger *integerValue = [NSNumber numberWithInteger:someInteger];
[dataToSave setObject:integerValue forKey:@"SomeIntegerValue"];

// Any objects that conform to NSCoding can be archived to an NSData instance.  In this example, MyClass conforms to NSCoding.
MyClass *someObject = [[MyClass alloc] init];
NSData *archivedStateOfSomeObject = [NSKeyedArchiver archivedDataWithRootObject:someObject];
[dataToSave setObject:archivedStateOfSomeObject forKey:@"SomeObject"];

// Create a serialized NSData instance, which can be written to a plist, from the data we've been storing in our NSMutableDictionary
NSString *errorDescription;
NSData *serializedData = [NSPropertyListSerialization dataFromPropertyList:dataToSave
                                                                    format:NSPropertyListXMLFormat_v1_0
                                                          errorDescription:&errorDescription];
if(serializedData) 
{
    // Write file
    NSError *error;
    BOOL didWrite = [serializedData writeToFile:plistPath options:NSDataWritingFileProtectionComplete error:&error];

    NSLog(@"Error while writing: %@", [error description]);

    if (didWrite)
        NSLog(@"File did write");
    else
        NSLog(@"File write failed");
}
else 
{
    NSLog(@"Error in creating state data dictionary: %@", errorDescription);
}

Loading

// Fetch NSDictionary containing possible saved state
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *plistPath;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                          NSUserDomainMask, YES) objectAtIndex:0];
plistPath = [rootPath stringByAppendingPathComponent:@"SavedState.plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSDictionary *unarchivedData = (NSDictionary *)[NSPropertyListSerialization
                                      propertyListFromData:plistXML
                                      mutabilityOption:NSPropertyListMutableContainersAndLeaves
                                      format:&format
                                      errorDescription:&errorDesc];

// If NSDictionary exists, look to see if it holds a saved game state
if (!unarchivedData)
{
    NSLog(@"Error reading plist: %@, format: %d", errorDesc, format);
} 
else 
{
    // Load property list objects directly
    NSString *myString = [unarchivedData objectForKey:@"MyString"];

    // Load primitives
    NSNumber *boolValue = [unarchivedData objectForKey:@"SomeBoolValue"];
    BOOL someBool = [boolValue boolValue];
    NSNumber *integerValue = [unarchivedData objectForKey:@"SomeIntegerValue"];
    BOOL someBool = [integerValue integerValue];

    // Load your custom objects that conform to NSCoding
    NSData *someObjectData = [unarchivedData objectForKey:@"SomeObject"];
    MyClass *someObject = [NSKeyedUnarchiver unarchiveObjectWithData:someObjectData];
}

For further reading, check out the Archives and Serialization Programming Guide.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top