Read and Write User Preferences

Reading and writing user preferences within iPhone applications is surprisingly easy given the NSUserDefaults class does most all the work for you.

What follows is a short example to show how you can read/write two values, a boolean and an integer. The example assumes you want to save state as to whether a user wants their username saved to the system and also saves their preferred startup screen (which tab in a tabber should be active when starting the application).

Let’s begin with a class I created to manage the preferences:

Preferences.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//
//  Preferences.h
//
#import <UIKit/UIKit.h>
 
@interface Preferences : NSObject 
{
}
 
+ (BOOL)shouldSaveUsername;
+ (NSInteger)startupTab;
+ (BOOL) setPreferences:(BOOL)saveUsername startupTab:(NSInteger)tabIndex;
 
@end

Notice that each method is defined as a class method (designated with the +). As you’ll see in the app delegate code that follows the advantage of this approach is that we don’t have to create an instance of Preferences in order to work with this object. The implementation of the Preferences class follows:

Preferences.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//
//  Preferences.m
//
#import "Preferences.h"
 
// This should probably be in a file with other constants
#define DEFAULT_TAB 0
 
@implementation Preferences
 
/*---------------------------------------------------------------------------
* Does user prefer to save their username to system?
*--------------------------------------------------------------------------*/
+ (BOOL)shouldSaveUsername
{
  // Does preference exist...
  if ([[NSUserDefaults standardUserDefaults] boolForKey:@"saveUsername"] != 0)
    return [[NSUserDefaults standardUserDefaults] integerForKey:@"saveUsername"];
  else
    return NO;
}
 
/*---------------------------------------------------------------------------
* Return the user preferred startup screen (which tab on tabbar)
*--------------------------------------------------------------------------*/
+ (NSInteger)startupTab
{
  // Does preference exist...
  if ([[NSUserDefaults standardUserDefaults] integerForKey:@"startupTab"] != 0)
    return [[NSUserDefaults standardUserDefaults] integerForKey:@"startupTab"];
  else
    return DEFAULT_TAB;   // Default startup tab
}
 
/*---------------------------------------------------------------------------
* Write preferences to system
*--------------------------------------------------------------------------*/
+ (BOOL) setPreferences:(BOOL)saveUsername startupTab:(NSInteger)tabIndex
{
  // Set values
  [[NSUserDefaults standardUserDefaults] setBool:saveUsername forKey:@"saveUsername"];
  [[NSUserDefaults standardUserDefaults] setInteger:tabIndex forKey:@"startupTab"];
 
  // Return the results of attempting to write preferences to system
  return [[NSUserDefaults standardUserDefaults] synchronize];
}
 
@end

There are two methods to get the current values for the preferences, shouldSaveUsername and startupTab. The method setPreferences writes both preferences to the system.

The code that follows shows how we can use the Preferences class:

SetPrefsAppDelegte.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
//  SetPrefsAppDelegate.h
//
#import <UIKit/UIKit.h>
 
@interface SetPrefsAppDelegate : NSObject <UIApplicationDelegate> 
{
  BOOL        saveUsername;
  NSInteger   preferredIndexInTabbar;
  UIWindow    *window;
}
 
@property (nonatomic, retain) IBOutlet UIWindow *window;
 
@end

In the interface I create two instance variables to reference the user preferences I am interested in tracking. Let’s look at the implementation file and see how to read/write these values:

SetPrefsAppDelegte.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//  SetPrefsAppDelegate.m
//
#import "SetPrefsAppDelegate.h"
#import "Preferences.h"
 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// App Delegate implementation
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@implementation SetPrefsAppDelegate
 
@synthesize window;
 
/*------------------------------------------------------
* Read preferences from system
*-----------------------------------------------------*/
-(void) loadPreferences
{ 
  saveUsername = [Preferences shouldSaveUsername];
  preferredIndexInTabbar = [Preferences startupTab]; 
}
 
/*------------------------------------------------------
* Application startup code
*-----------------------------------------------------*/
- (void)applicationDidFinishLaunching:(UIApplication *)application 
{   
  // Load user preferences (notice the default values the first time through)
  [self loadPreferences];
 
  // Show the current values of the preferences
  NSLog(@"Save user preferences: %s", saveUsername == YES ? "Yes" : "No");
  NSLog(@"Preferred startup tab: %d", preferredIndexInTabbar);
 
  // This is a little contrived, but you get the point...
  BOOL saveUname = YES;
  NSInteger index = 3;
 
  // Write new values to the sytem
  [Preferences setPreferences:saveUname startupTab:index];
 
//  NSLog(@"Home: %s", NSHomeDirectory());
//  NSLog(@"Home: %@", NSHomeDirectory());
 
  // Override point for customization after application launch
  [window makeKeyAndVisible];
}
 
- (void)dealloc 
{
  [window release];
  [super dealloc];
}
 
@end

The first time running the application, the output will look as shown below:

Once I exit and re-start the application, the output now shows the values that I saved previously.

There you have it, a no frills way to read/write user preferences.

  1. You can combine this with the plist based Settings Bundles to do some useful things. For example, I have a toggle switch that will reset the data for our app _once_. The setting is checked at startup and then forced to false.

    I read somewhere that the defaults get saved periodically, but found that it was best to use [[NSUserDefaults standardUserDefaults] synchronize] to make sure they indeed got flushed.

  2. Its also worth mentioning that if you in combination with NSCoder you can save even complex data types in the user defaults, not just primitive key-value types. This can be very handy when trying to keep track of a user session so that you can “restore” it if the app is abruptly exited.

  3. If I deploy something to the AppStore and use this method to save, for example the top scores of my game and later I put out a new version of the game, will the users lose their scores by installing the new version?

  4. It’s my understanding that NSUserDefaults will be saved across updates to an application.

    Can anyone point to a specific document to confirm?

    • Objects stored by your app with NSUserDefaults (or Core Data, etc.) will not be deleted if a user upgrades to a newer version of your app. But, they will be deleted if the user deletes your app from their device.

  5. Shouldn’t this code:

    if ([[NSUserDefaults standardUserDefaults] integerForKey:@”startupTab”] == 0

    be:

    if ([[NSUserDefaults standardUserDefaults] integerForKey:@”startupTab”] != 0

    not equal to 0 so then that means if it is defined return the saved value and otherwise return the default…??

    • Good call Alex, looks like this got messed up during copy/paste from a project. I’ve updated the code example. Thanks.

Comments are closed.