Saving and loading user data and preferences

Oct 7, 2009   //   by Rob Segal   //   Development  //  8 Comments

There is a collectively recognized method for storing application data and preferences which is NSUserDefaults.  The API for this is a bit unconventional and I thought an appropriate wrapper class would work well in this case.  While not completely understanding the full details of NSUserDefaults I developed a singleton class which we can use in our games to save/load data.  The interface is simple…

SettingsManager.h

#import <Foundation/Foundation.h>
 
@interface SettingsManager : NSObject {
	NSMutableDictionary* settings;
}
 
-(NSString *)getString:(NSString*)value;
-(int)getInt:(NSString*)value;
-(void)setValue:(NSString*)value newString:(NSString *)aValue;
-(void)setValue:(NSString*)value newInt:(int)aValue;
-(void)save;
-(void)load;
-(void)logSettings;
 
+(SettingsManager*)sharedSettingsManager;
@end

SettingsManager.m

#import "SettingsManager.h"
 
@implementation SettingsManager
 
static SettingsManager* _sharedSettingsManager = nil;
 
-(NSString *)getString:(NSString*)value
{	
	return [settings objectForKey:value];
}
 
-(int)getInt:(NSString*)value {
	return [[settings objectForKey:value] intValue];
}
 
-(void)setValue:(NSString*)value newString:(NSString *)aValue {	
	[settings setObject:aValue forKey:value];
}
 
-(void)setValue:(NSString*)value newInt:(int)aValue {
	[settings setObject:[NSString stringWithFormat:@"%i",aValue] forKey:value];
}
 
-(void)save
{
        // NOTE: You should be replace "MyAppName" with your own custom application string.
        //
	[[NSUserDefaults standardUserDefaults] setObject:settings forKey:@"MyAppName"];
	[[NSUserDefaults standardUserDefaults] synchronize];	
}
 
-(void)load
{
        // NOTE: You should be replace "MyAppName" with your own custom application string.
        //
	[settings addEntriesFromDictionary:[[NSUserDefaults standardUserDefaults] objectForKey:@"MyAppName"]];
}
 
// Logging function great for checkin out what keys/values you have at any given time
//
-(void)logSettings
{
	for(NSString* item in [settings allKeys])
	{
		NSLog(@"[SettingsManager KEY:%@ - VALUE:%@]", item, [settings valueForKey:item]);
	}
}
 
+(SettingsManager*)sharedSettingsManager
{
	@synchronized([SettingsManager class])
	{
		if (!_sharedSettingsManager)
			[[self alloc] init];
 
		return _sharedSettingsManager;
	}
 
	return nil;
}
 
+(id)alloc
{
	@synchronized([SettingsManager class])
	{
		NSAssert(_sharedSettingsManager == nil, @"Attempted to allocate a second instance of a singleton.");
		_sharedSettingsManager = [[super alloc] init];
		return _sharedSettingsManager;
	}
 
	return nil;
}
 
-(id)autorelease {
    return self;
}
 
-(id)init {	
	settings = [[NSMutableDictionary alloc] initWithCapacity:5];	
	return [super init];
}
 
@end

Values can then be saved/retrieved in code like so…

[[SettingsManager sharedSettingsManager] saveValue:@"hiscore" newValue:@"10000"];
NSString* myValue = [[SettingsManager sharedSettingsManager] getValue:@"hiscore"];
int myInt = [[SettingsManager sharedSettingsManager] getInt:@"challenges_won"];

To save our values on an incoming call or other system interruption we can put save/load calls in the appropriate callbacks of the app delegate…

- (void)applicationDidFinishLaunching:(UIApplication *)application
 
{
  // ...  (some code) ...
 
  // Load settings on initial startup
  //
  [[SettingsManager sharedSettingsManager] load];
 
  // ...  (more code) ...
}
 
- (void)applicationWillResignActive:(UIApplication *)application
{
  [[SettingsManager sharedSettingsManager] save];
  [[Director sharedDirector] pause];
}
 
- (void)applicationDidBecomeActive:(UIApplication *)application
{
  [[SettingsManager sharedSettingsManager] load];
  [[Director sharedDirector] resume];
}
 
- (void)applicationWillTerminate:(UIApplication *)application
{
  [[SettingsManager sharedSettingsManager] save];
  [[Director sharedDirector] end];
}

This basic class is great for storing arbitrary user state values that you want to preserve for the next time someone plays your game.  Hi scores, level progression, number of lives are a few of the possibilities this class could be used for.

8 Comments

  • I’m using this right now for an app, it’s great. Though the post above needs updating, it’s full of typos that will throw off new comers, especially if they copy and paste.

    (eg. declaring “setValue” and then saying to use “saveValue” to save data)

  • for(NSString* item in [settings allKeys])

    should be written as

    for(NSString* item in settings)

  • Great Tutorial! I’ve been search for something like this!

    I do have a question though. When I run my app with leak tool in Xcode, it says there are leaks. Do I need to de allocate something in the end?

  • these are what I get in the instrument…

    Leaked Object # Address Size Responsible Library Responsible Frame
    Malloc 32 Bytes 4 128 libLLVMContainer.dylib llvm::SmallVectorImpl::grow(unsigned long)
    Malloc 32 Bytes 4 128 libLLVMContainer.dylib llvm::SmallVectorImpl::grow(unsigned long)
    Malloc 512 Bytes 0x982f400 512 AudioToolbox AU3DMixerEmbeddedInputElement::Initialize()
    Malloc 32 Bytes 0x642b000 32 TabToDeath -[SettingsManager init]
    Malloc 32 Bytes 0x642afe0 32 TabToDeath -[SettingsManager init]
    __NSCFDictionary 0x642afb0 48 TabToDeath -[SettingsManager init]

    Not sure why I’m getting in the init method.

  • Awesome, saved me hours of work!

    1 note: according to the class definition, should be setValue and not saveValue.

    Also, if you want to have some default values for settings when the app is first installed, maybe try:

    BOOL defaultsSaved = [[SettingsManager sharedSettingsManager] getInt:@”defaultsSaved”];
        if(!defaultsSaved) {
            [[SettingsManager sharedSettingsManager] setValue:@”defaultsSaved” newInt:1];
            [[SettingsManager sharedSettingsManager] setValue:@”sFxVol” newFloat:0.8];
            [[SettingsManager sharedSettingsManager] setValue:@”sBgVol” newFloat:1.0];
        }

  • I added the following to support returning a value if one is not already found:

    -(NSString *)getString:(NSString*)value defaultString:(NSString*)defaultValue
    {
    NSString *result = [settings objectForKey:value];
    if(result == nil)
    result = defaultValue;

    return result;

    }

  • [...] pesquisando uma maneira fácil de salvar dados em minhas aplicações para iOS, achei este link, e pareceu ser bem útil, queria [...]

  • I can’t seem to get this working in my code. Do I have to do more than import the SettingsManager.h into my class? When i try to call functions from the settings manager is gives me no issues but nothing happens.

Leave a comment

Our Games

Latest Tweets