iOS CCUserDefault not saving between sessions.

I’m trying to use CCUserDefault to save user settings and which levels have been completed in my game but I’m having a couple of problems.

User settings save correctly between sessions, but the completed levels do not. I am definitely calling flush and I data is definitely being stored during a single session so I have no Idea what could be the problem. Can anyone offer a suggestion?

Code for settings being saved:

void SaveVolume(){
CCUserDefault::sharedUserDefault()->setFloatForKey(“SFXVol”, (m_pSFXSlider->GetValue() * 0.01f));
CCUserDefault::sharedUserDefault()->flush();
}

void SetVolumeFromSavedData(){ fSFXVolume = CCUserDefault::sharedUserDefault()-\>getFloatForKey(“SFXVol”); }

Code for Level Data being Saved:

void SaveLevelAsCompleted(){
ostringstream s2;
s2 << (m_iCurrentArea) << “Level” << (m_iCurrentLevel) << “*Unlocked";
string strLevelUnlocked);
CCUserDefault::sharedUserDefault->setBoolForKey, true);
CCUserDefault::sharedUserDefault->flush;
}


void IsLevelUnlocked{
ostringstream s;
s << << “Level” << << "*Unlocked”;
string strLevelUnlocked(s.str());

bool bUnlocked = CCUserDefault::sharedUserDefault()->getBoolForKey(strLevelUnlocked.c_str());
}

As I said this works fine in a single session, but when I start a new one all previous level data but not user settings have not saved or are at least not being recovered. Can anyone tell me why?

Hi again guys, I’m sorry to do this but seen as how I’m no further forward in solving this issue I thought I’d bump this post.

After further investigation I’ve noticed where the problem is, but not why it happens or how to fix it.

Basically when I call getBoolForKey(const char* pKey, bool defaultValue) I am certain I pass in the correct values for the variable (i.e. no spelling errors) but when that method calls getValueForKey(pKey) it fails to get an XMLNodePtr. I don’t know why this fails between sessions and not the rest of the time (I’ve walked through the code in both cases, it works fine until I start a new session where it fails). Once again, I am SURE I am calling flush so my question is are there any other reasons why getValueForKey(pKey) would fail to get an XMLNodePtr via getXMLNodeForKey(pKey, &rootNode)?

For completeness here is the getValueForKey(pKey) method:-

`static inline const char* getValueForKey(const char* pKey)
{
const char* ret = NULL;
xmlNodePtr rootNode;
xmlNodePtr node = getXMLNodeForKey(pKey, &rootNode);

// find the node
if (node) //Node is always NULL after I start a new session
{
ret = (const char*)xmlNodeGetContent(node);
}
return ret;
}`

Really? nobody can offer any advice?

Have you tested on other platforms?

When you say a new session, are you just running from xCode again or just hitting the icon in the simulator?

Stupid question, you’re not uninstalling the app from the simulator are you?

Hi Adam,

I’ve not tested on other platforms yet. I’m currently developing and testing on my iPhone 3GS and not the simulator.

When I say start a new session I mean hitting the icon, playing the game, hitting the home button, double clicking the home button, ending that instance of the game running, then hitting the icon again (I can’t remember what this process is called) If I just click home then reopen the app, the saved data is as I left it.

Hahaha I’m definitely not uninstalling the app, although I’m hoping it is something that stupid and easy to fix.

I’m using the same system to store my music and sound volume settings.

I have only tested in Windows, Android and iOS simulator (not on an actual iOS device).
I have not had any instances of losing the setting.

The only time I have seen it lose the settings is if I uninstall the app.

My code looks like this (GameManager is a singleton class):-

int GameManager::GetSetting_musicVolume()
{
    return CCUserDefault::sharedUserDefault()->getIntegerForKey("musicVolume", 50);
}

void GameManager::SetSetting_musicVolume(int musicVolume)
{
    musicVolume = MIN(musicVolume, 100);
    musicVolume = MAX(musicVolume, 0);
    CCUserDefault::sharedUserDefault()->setIntegerForKey("musicVolume", musicVolume);
    CCUserDefault::sharedUserDefault()->flush();
}

When I use it for my volume settings it works perfectly, but not for storing which levels are unlocked.

I do it in exactly the same way as you. Its the inconsistency that is most annoying.

There is a function in CCUserDefault, getXMLFilePath.

It might be worth loading the file and writing out the contents.
See what is stored when it stops working (i.e after killing the app).

I’m storing my per level info in a binary file so don’t have any code to compare.

Hi guys, I’m back on this bug after a week or two on another few bugs.

I did what you suggested and it looks like you were right. Printing out the XML file looks like this:-

<?xml version="1.0"?> true 0.000000 0.000000 0\_Level\_2\_Unlocked>true0\_Level\_7\_Unlocked>true0\_Level\_7\_Unlocked>true0\_Level\_7\_Unlocked>true0\_Level\_2\_Unlocked>true

VolumeSet, BGVol and SFXVol all look legitamte while the level data looks FUBAR.

Its nice knowing that it is the problem, but I don’t know how to fix it?

I’m not sure what would cause that. (you are getting > out of the file or has the forum done that?)

Unless someone else can suggest something I would suggest:-

Debug as far in as you can get and see if you can spot anything.

Change the name you using for each info (could be underscore causing it?).

Save only 1 item and see if that gets saved correctly (it looks like your 0_Level_7_Unlocked in inside 0_Level_2_Unlocked).
There looks to be 3 0_Level_7_Unlocked tags which is strange.

Change the type (int maybe, using 1 and 0 for true and false) and see if that is causing it.

Thanks for your help.

I have no idea why, but it was the name I was using it was objecting to. It didn’t like starting with a number (0_Level_2 etc) so I changed it to Area_0_Level_2_Unlocked and it works now.

I have no idea why it doesn’t like the number at the start, but its an easy work around to fix the problem.

1 Like

I’ve just sorted out a similar problem, except I had spaces in my key.

I’m assuming it comes down to the rules about what can be used in XML elements;

from w3schools;
* Names can contain letters, numbers, and other characters
* Names cannot start with a number or punctuation character
* Names cannot start with the letters xml (or XML, or Xml, etc)
* Names cannot contain spaces

an assert might have been nice, or even something in the docs…

1 Like

Nice one, I didn’t think of looking at the xml spec.