UserDefaults remove saves

Hello,

For saving data in game I am using UserDefaults. Saving is working as expected but I tried removing UserDefaults data and it is not working.

To remove UserDefault data I am using:

remove(userDefaults->getXMLFilePath().c_str());

After some time looking into problem I found that there there might be problems with paths.

log("Saves path: %s", userDefaults->getXMLFilePath().c_str());

Returned path:
…/08504990-503A-489D-A6ED-A7F757200407/Library/Caches/UserDefault.xml
I looked into that folder and saw that there is no UserDefault.xml
Instead on:
…/08504990-503A-489D-A6ED-A7F757200407/Library/Preferences
I find a .plist with all my saves.

Also userDefaults->isXMLFileExist() returns false

Do I miss something?
Or there is other, better, easier way to delete saves?

I believe on macOS it’s now using Apple’s NSUserDefault directly. The XML stuff I think was just left in the code base in case you’ve previously released a version of your game that was using the “older” writing file to XML document method.

Android is similar in that new games can use the JNI user preferences specific to its platform.

Search for KEEP_COMPATABILITY to understand this further.

Just make sure you continue to use the same method (UserDefault.xml or Platform-Specific Preferences) with every update of your game, unless you write migration code to move to the other method (similar to how you manage saved games over updates).

Hope that helps.

Thanks for information, it was helpfull. :slight_smile:

What about deleting saves?

I can not find a way to do that.
Only way I find to delete saves is:

userDefaults->deleteValueForKey(<#const char *key#>)

You may want to write save games as individual files, or look into using a database such as sqlite.

If your save state is relatively simple and you want to use UserDefault then I’d probably suggest converting your data to and from an ascii string, a base64 string, or possibly use the Data class with setDataForKey.

Regardless you’ll need to serialize your data in and out. There’s string formats, or you could use a tool such as FlatBuffers/MsgPack/etc, or JSON/XML, or your own game-specific custom binary/string format, etc.

Any more specific advice would probably be a disservice as you’ll need to look at your specific game and look into how you want to handle storing, loading, possibly serializing (parse to/from), and for longer-term updates migration/versioning.

Oh, and then to delete any specific save you would just deal with it by deleting a file, or if you serialize to string which you store as a single value in UserDefault then you can just delete it by the save’s id, or if you use Sqlite then you’ll just do a SQL DELETE command (or related), etc.

Thanks for a detailed answer.

I am already using UserDefault to save all my game data it works as expected. I just can not find the way built to remove data from userdefaults. (example: reset game saves and start game from start)

To reset UserDefault on apple I just use :

NSString *domainName = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:domainName];

But what about android, windows ?

Maybe post an feature request over on the github. It’s probably a simple extension to add android/windows support for removing all preferences. You could even submit a PR if you write all the code (or post your solutions here).

My best guess is that most used this for only a few key/value pairs or otherwise stored lots of data in only a few or single root key so that deleting is as easy as removing the data for that key.

I always focus on getting things done so I’d just handle this in whatever way makes sense to me. I like cocos2d-x in large part because I don’t have to use the “standard” method for anything if desired (or if easier to use other method(s)).

I am resetting UserDefaults like this.

/* Reset userdefaults */

auto userDefaults = cocos2d::UserDefault::getInstance();

std::string sharedPreferencesFileLocation = "/data/data/PACKAGE_NAME/shared_prefs/Cocos2dxPrefsFile.xml";

if (FileUtils::getInstance()->isFileExist(sharedPreferencesFileLocation)) {
    
    remove(sharedPreferencesFileLocation.c_str());
} else if (FileUtils::getInstance()->isFileExist(userDefaults->getXMLFilePath())) {
    
    remove(userDefaults->getXMLFilePath().c_str());
} else {
    
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MACOS)
    userDefaults->resetData();
#endif
}

resetData() in CCUserDefault-apple.mm:

void UserDefault::resetData() {
    
    NSString *domainName = [[NSBundle mainBundle] bundleIdentifier];
    [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:domainName];
}

@stevetranby do you think there is some kind of scenerio this might not work? (For: ios, android, win32, macos, xbox)

At a glance, that appears correct. I know I’ve glanced at the remove() function and added a github issue question regarding making it more cross-platform.

The only thing I think of at a glance is that the iOS/Mac section should wrap and ignore the entire file test/remove if block, especially since the /data/data is Android only (I believe).

??

Thanks I will adjust it.

Warning: you may need to keep the getXMLFilePath section in all platforms (incl. ios/mac) to keep backward compatibility?

What about reseting saves for CC_PLATFORM_WINRT

Should I in CCUserDefault-winrt.cpp:

void UserDefault::reserData() {

    ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings;
    auto values = localSettings->Values;

    // Delete values somehow?

    flush();
}

How values could be deleted?

Or ApplicationData::Current->ClearAsync(); should be enought?