Loading and Saving Game Data on the iPhone / iPod Touch
by Dave Calabrese · 05/05/2009 (9:54 pm) · 8 comments
There is no doubt that if you are new to iPhone / iPod Touch programming and development, and you've gotten your code to work great on Windows and / or OS X, you may be tearing your hair out when you determine that it just isn't saving on the actual iPhone / iPod Touch. There are a number of reasons here... but if you've come to this conclusion, then chances are you were also trying to save out a file to the device and finding it isn't working. The reason for that is you need to use special engine-level code to determine the location of the app directory, as you cannot save anywhere else on the device for security reasons.
So, there are a number of ways that you can store data on the iPhone / iPod Touch. You can store it as a file - which needs to be in the Documents directory, or you can save it in NSUserDefaults.
To you Windows programmers, NSUserDefaults is much like the registry - you can save data in keys and load it back when you need it. Much like the registry, you probably would not want to save the entire contents of War and Peace to a key, but it should give you more than enough to store a decent amount of game state data - in most cases, it should be the only save option you need.
So enough of my prattle - let's move on with the actual resource here, shall we?
In the engine code for iTGB, open up /platformiPhone/iPhoneFileio.mm and find a nice place to add in the following block of code (and make sure you do not add it inside the #if defined(TORQUE_DEBUG) section of code or else this will only work while you are in debug mode!)
What this will do is expose 2 functions to the TorqueScript:
<previouslySavedData> = loadGameDataFromDevice();
saveGameDataToDevice(<dataToSave>);
Those should be pretty self explanatory what they do. All you then need to do is integrate the two functions into your TorqueScript and create a single string of data that you send in to save, and parse out when you retrieve it!
The only other thing you need to do now is define the name of the key that you are going to save your data at. This should be rather unique to make sure you are not overwriting the data from another app: so something like 'mySaveGameData' is bad - which is exactly what the key in this code says! So, before you compile, go up into the Load and Save functions in the engine and change 'mySaveGameData' in the two places its found (once in each function) to something very unique (such as "awesomeTGBGameWithFeralKittens".... or something better).
And there you have it! Saving game data in NSUserDefaults!
This resource could not have been done without the following people who helped me as I struggled through my first few days with Objective-C and Cocoa programming:
- Bret Patterson
- Ronny Bangsund
- Marc Schaerer
- Brandon of iCodeBlog and his post on saving and retrieving data from NSUserDefaults
If you happen to see them somewhere, buy them a beer. Or several - they are quite the awesome people.
Feel free to improve upon this code and expand on it!
So, there are a number of ways that you can store data on the iPhone / iPod Touch. You can store it as a file - which needs to be in the Documents directory, or you can save it in NSUserDefaults.
To you Windows programmers, NSUserDefaults is much like the registry - you can save data in keys and load it back when you need it. Much like the registry, you probably would not want to save the entire contents of War and Peace to a key, but it should give you more than enough to store a decent amount of game state data - in most cases, it should be the only save option you need.
So enough of my prattle - let's move on with the actual resource here, shall we?
In the engine code for iTGB, open up /platformiPhone/iPhoneFileio.mm and find a nice place to add in the following block of code (and make sure you do not add it inside the #if defined(TORQUE_DEBUG) section of code or else this will only work while you are in debug mode!)
//----------------------------------------------------------------------------
// Loading and Saving of data to the NSUserDefaults on the iPhone / iPod Touch
// Resource by Dave Calabrese of Gaslight Studios
// - This resource gives some huge credit to those who helped me to get this
// to work in the first place. Those people are:
// - Bret Patterson
// - Ronny Bangsund
// - Marc Schaerer
// - Brandon of iCodeBlog and his post on saving and retrieving data from NSUserDefaults
void saveGameDataToDevice(const char* dataString)
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *convertedString = [[NSString alloc] initWithUTF8String:dataString];
[prefs setObject:convertedString forKey:@"mySaveGameData"];
[prefs synchronize];
}
static const char* loadGameDataFromDevice(void)
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *myString = [prefs stringForKey:@"mySaveGameData"];
return [myString UTF8String];
}
ConsoleFunction(saveGameDataToDevice, void, 2, 2, "(string) Save's the game data to a key defined in the Objective-C engine code.")
{
//Con::printf("@@@ Engine acquired save data: %s",argv[1]); //Uncomment this to see the data being saved.
saveGameDataToDevice( argv[1] );
}
ConsoleFunction(LoadGameDataFromDevice, const char*, 1, 1, "Load the game data from a key defined in the Objective-C engine code.")
{
const char* result = loadGameDataFromDevice();
return result;
}What this will do is expose 2 functions to the TorqueScript:
<previouslySavedData> = loadGameDataFromDevice();
saveGameDataToDevice(<dataToSave>);
Those should be pretty self explanatory what they do. All you then need to do is integrate the two functions into your TorqueScript and create a single string of data that you send in to save, and parse out when you retrieve it!
The only other thing you need to do now is define the name of the key that you are going to save your data at. This should be rather unique to make sure you are not overwriting the data from another app: so something like 'mySaveGameData' is bad - which is exactly what the key in this code says! So, before you compile, go up into the Load and Save functions in the engine and change 'mySaveGameData' in the two places its found (once in each function) to something very unique (such as "awesomeTGBGameWithFeralKittens".... or something better).
And there you have it! Saving game data in NSUserDefaults!
This resource could not have been done without the following people who helped me as I struggled through my first few days with Objective-C and Cocoa programming:
- Bret Patterson
- Ronny Bangsund
- Marc Schaerer
- Brandon of iCodeBlog and his post on saving and retrieving data from NSUserDefaults
If you happen to see them somewhere, buy them a beer. Or several - they are quite the awesome people.
Feel free to improve upon this code and expand on it!
#4
And thanks for the 'full star' rating, hahaha! :)
05/06/2009 (5:42 am)
@Ronny: You gave suggestion on some code items which helped to point me in the right direction while trudging through the murky swamps of "Intro to Objective-C". So credit you did score. And thanks for the 'full star' rating, hahaha! :)
#5
Does everything have to be thrown into one superduper string to be written and read with one command each, or can you make separate commands like this:
then followed by
UPDATE Never mind, I realized what you meant by 'one string of data' that you can parse out later. Unfortunately, I need to save a lot more information than that, but this is helpful for most people anyway. Thanks.
06/05/2009 (4:09 am)
I have a TON of data to save, including an array of strings.Does everything have to be thrown into one superduper string to be written and read with one command each, or can you make separate commands like this:
saveGameDataToDevice($score); saveGameDataToDevice($numberOfLives); saveGameDataToDevice($monstersRemaining);
then followed by
$score=loadGameDataFromDevice(); $numberOfLives=loadGameDataFromDevice(); $monstersRemaining=loadGameDataFromDevice();
UPDATE Never mind, I realized what you meant by 'one string of data' that you can parse out later. Unfortunately, I need to save a lot more information than that, but this is helpful for most people anyway. Thanks.
#6
You can save multiple strings using this technique. So where we are saving the string at "mySaveGameData" in the Objective-C, you could have "mySaveGameDataA", "mySaveGameDataB", "mySaveGameDataC" and so forth.
08/25/2009 (9:39 am)
(VERY late response but...)You can save multiple strings using this technique. So where we are saving the string at "mySaveGameData" in the Objective-C, you could have "mySaveGameDataA", "mySaveGameDataB", "mySaveGameDataC" and so forth.
#7
09/07/2009 (3:21 pm)
I made a small modification in order to have real "key - value" storing. Hope this helps.//----------------------------------------------------------------------------
// Loading and Saving of data to the NSUserDefaults on the iPhone / iPod Touch
// Resource by Dave Calabrese of Gaslight Studios
// - This resource gives some huge credit to those who helped me to get this
// to work in the first place. Those people are:
// - Bret Patterson
// - Ronny Bangsund
// - Marc Schaerer
// - Brandon of iCodeBlog and his post on saving and retrieving data from NSUserDefaults
// Improved by Stefan Eilers (IntelligentMobiles)
void saveGameDataToDevice(const char* dataString, const char* keyString)
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *convertedString = [[NSString alloc] initWithUTF8String:dataString];
NSString *convertedKeyString = [[NSString alloc] initWithUTF8String:keyString];
NSLog( @"saveGameDataToDevice - Key: %@, value: %@", convertedKeyString, convertedString );
[prefs setObject:convertedString forKey:convertedKeyString];
[prefs synchronize];
}
static const char* loadGameDataFromDevice( const char* keyString )
{
NSLog( @"loadGameDataFromDevice: %s", keyString );
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *convertedKeyString = [[NSString alloc] initWithUTF8String:keyString];
NSString *myString = [prefs stringForKey:convertedKeyString];
NSLog( @"Return: %@ for key: %@", myString, convertedKeyString );
return [myString UTF8String];
}
ConsoleFunction(saveGameDataToDevice, void, 3, 3, "(string, string) Save's the game data string to a key.\n"
"@param string String to store\n"
"@param key String used as key")
{
saveGameDataToDevice( argv[1], argv[2] );
}
ConsoleFunction(loadGameDataFromDevice, const char*, 2, 2, "(key) Load the game data string from a key.\n"
"@param string String to store\n"
"@return Previously stored string")
{
const char* result = loadGameDataFromDevice( argv[1] );
return result;
}
#8
UPDATE : I have figured it out, just needed a good rest :)
10/20/2009 (6:43 pm)
Firstly a huge thank you for the above script it was really helpful. But I wanted to save multiple values, for e.g lastplayedlevel, levelscompleted etc. I have managed to save one but how do I save multiple fields.UPDATE : I have figured it out, just needed a good rest :)
Torque Owner Brian McRae