The following is a method utilizing NSUserDefaults and the iPhone SDK version 2.2.1 or later to save, retrieve and sort high scores for the iPhone/iPod touch. This example instructs on the design concept, not the syntax. It assumes the reader understands the language and conventions of the iPhone SDK. This approach is featured in TileTouch for the iPhone and iPod touch.

Note: One should decide on a convention of variables, or a system to abide by when coding, which makes individual and multiple projects consistent and easier to understand. For instance, the code displayed on this site does not use x or y for counters as they are reserved for coordinates to avoid possible confusion. The letter "i" works as an abbreviation for "iteration" and is also visually easy to discern, especially when multiple counters are used as it looks similar to roman numerals i.e. "i, ii, iii" and is easier on the eyes than something like "w, ww, www". This is largely a matter of personal preference, but choose a system and stick to it.

The code (explanation below):

int i, ii = -1; struct high_score_entry { NSString *name; int highScore; }; struct high_score_entry structArray[10]; NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults]; for (i = 0; i < 10; i++) { if ([userPreferences stringForKey:[NSString stringWithFormat:@"highScoreNameEntry%d", i]] != nil && [userPreferences stringForKey:[NSString stringWithFormat:@"highScoreEntry%d", i]] != nil) { structArray[i].name = [userPreferences stringForKey:[NSString stringWithFormat:@"highScoreNameEntry%d", i]]; structArray[i].highScore = [userPreferences integerForKey:[NSString stringWithFormat:@"highScoreEntry%d", i]]; ii = i; } } if (score > 0) { for (i = ii; i >= 0; i--) { if (score > structArray[i].highScore) { if (i < 9) structArray[i + 1] = structArray[i]; structArray[i].name = scoreNameEntry; structArray[i].highScore = score; if (i == ii && ii < 9) ii = i + 1; } else if (i == ii && i < 9) { structArray[i + 1].name = scoreNameEntry; structArray[i + 1].highScore = score; ii = i + 1; } } } if (ii == -1 && score > 0) { structArray[0].name = scoreNameEntry; structArray[0].highScore = score; ii = 0; } for (i = 0; i <= ii; i++) { [userPreferences setObject:structArray[i].name forKey:[NSString stringWithFormat:@"highScoreNameEntry%d", i]]; [userPreferences setInteger:structArray[i].highScore forKey:[NSString stringWithFormat:@"highScoreEntry%d", i]]; }

First, two integers are set to use as iteration "counters". It will be clearer further down why two are needed, but suffice for now "int i" will be something of a "dumb" counter which blindly runs through its designated range while "int ii" will be tied specifically to the number of entries in the high score list.

int i, ii = -1;


Next a struct is defined to contain the values associated with each high score entry. For this example there are only two entries: the player's name and the player's score. However, this method is entirely flexible and any number of values can be introduced such as level achieved, difficulty, etc.

struct high_score_entry { NSString *name; int highScore; };


An array is set to contain a struct, or set of values, for each entry in the high score list. In this case the array is defined for 10 entries but the list could easily be a top 8 as featured in the game, or any other number. This is followed by defining NSUserDefaults. See the iPhone SDK for the NSUserDefault class if needed.

struct high_score_entry structArray[10]; NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];


The working part of the save/retrieve/sort follows and is a bit more involved. The purpose of the first block of code is to determine if any score entries currently exist, how many, and retreive them.

The "dumb" counter "int i" runs through the 10 possible rankings or "slots" in the high score list, first checking to see if a value for the given slot already exists in the NSUserDefaults. If so, those values are retrieved and stored in one of the array structures for that slot in order to be evaluated against the current score. If not, the next iteration occurs. If no slot exists or is blank, "ii" remains set to "-1" which will indicate no scores yet exist. If there are any existing slots, "ii" will match "i" until "i" runs out of slots, thus setting the number of slots which currently exist.

for (i = 0; i < 10; i++) { if ([userPreferences stringForKey:[NSString stringWithFormat:@"highScoreNameEntry%d", i]] != nil && [userPreferences stringForKey:[NSString stringWithFormat:@"highScoreEntry%d", i]] != nil) { structArray[i].name = [userPreferences stringForKey:[NSString stringWithFormat:@"highScoreNameEntry%d", i]]; structArray[i].highScore = [userPreferences integerForKey:[NSString stringWithFormat:@"highScoreEntry%d", i]]; ii = i; } }

The next block of code entails the "bubble sort", determining if and where the current score being evaluated resides in relation to the existing scores.

If the current score is not greater than zero it will not be added to the high score list and there is no need to continue this block of code. Otherwise the counter is set equal to "int ii" (the last score entry if there is one, else "ii" will still be set at "-1" and "i = ii" will fail "i >= 0") and works it's way higher up the list to the top entry at structArray[0]. If the current score is greater than the saved score at the slot being evaluated, the saved slot moves down the list one place ("structArray[i + 1]") so long as the current saved slot is not the last slot in the array ("i < 9"), lest it run past the array. The current score then replaces the previous entry. This continues until the current score either makes it to the top of the list or is compared to an existing score it is not greater than, at which point the routine has nothing else to do.

If the current score is not higher than the last entry ("i == ii") on the list to begin with and the list is less than 10 slots ("i < 9"), and thus there is room left in the array/list to be filled, the current score is added to the list as the new last entry ("ii = i + 1").

if (score > 0) { for (i = ii; i >= 0; i--) { if (score > structArray[i].highScore) { if (i < 9) structArray[i + 1] = structArray[i]; structArray[i].name = scoreNameEntry; structArray[i].highScore = score; if (i == ii && ii < 9) ii = i + 1; } else if (i == ii && i < 9) { structArray[i + 1].name = scoreNameEntry; structArray[i + 1].highScore = score; ii = i + 1; } } }


The last block of the sort code is the alternate branch for no existing scores ("ii = -1"). So long as the current score is greater than zero, it becomes the new first entry in a new score list.

if (ii == -1 && score > 0) { structArray[0].name = scoreNameEntry; structArray[0].highScore = score; ii = 0; }


Finally the updated list is saved, writing slot data to NSUserDefaults until there are no slots left to write ("i <= ii").

for (i = 0; i <= ii; i++) { [userPreferences setObject:structArray[i].name forKey:[NSString stringWithFormat:@"highScoreNameEntry%d", i]]; [userPreferences setInteger:structArray[i].highScore forKey:[NSString stringWithFormat:@"highScoreEntry%d", i]]; }



Rightful Recognition: TileTouch, the iPhone/iPod touch application this method was used for, includes a special thank you viewable in the application "about" page to an individual who assisted on an early sort/save/retrieve method prior to using NSUserDefaults for the current method. While this method supercedes the previous one, the individual made a contribution deserving of mention. Intellectual property has great value, and those who contribute to it deserve to be recognized. Readers are welcome to use this code and/or it's design concept for any and all applications, but if so, please include a direct mention to "Jesse Widener" and/or "www.artandstructure.com" viewable in the application itself.