Implementing preferences in iOS – the right way
A few weeks ago, I shared how I implemented preferences in my Morse Trainer app – the wrong way. Since then, I’ve learned, and I present here a brief synopsis on how to do preferences the right way.
I’m getting ready to update the app. I’ll be taking out the tabbed view and giving access to the preferences/about page via a button. While I expect the Settings page itself to look pretty much the same as before, the insternal storage location of the two settings – code sending speed and text source – will move from a user-space simple file to the User Defaults system. In the transition, the code will get a bit less complex.
Looking back at the old code, I see it’s not really as bad as I thought. At least I’m using using a keyed archive data structure. Let’s see how we can clean this up a bit, though.
Here’s how we’d rewrite last time’s code snippets.
What we did before:
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; . . . // Get the current stored preferences // Get the full path of our preferences data archive file NSString *prefsDataPath = [self preferencesDataPath]; // Attempt to unarchive it PreferencesData *preferencesData = [NSKeyedUnarchiver unarchiveObjectWithFile:prefsDataPath]; // If it didn't exist, set it to defaults if (!preferencesData) { // Set to the default data sendSpeed = 2; // fast textSource = 0; // quotes } else { sendSpeed = [preferencesData sendSpeed]; textSource = [preferencesData textSource]; } . . . }
And what we do now:
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; . . . sendSpeed = [[NSUserDefaults standardUserDefaults] stringForKey:@"sendSpeed"]; textSource = [[NSUserDefaults standardUserDefaults] stringForKey:@"textSource"]// If there's not already data there, the system will return nil, so we set it here if (sendSpeed == nil) sendSpeed = 2; // fast if (textSource == nil) textSource = 0; // quotes . . . }
When it’s time to save, here’s what we did:
-(IBAction) sourceControlIndexChanged {textSource = self.sourceControl.selectedSegmentIndex;// Save the current preferences data to disk PreferencesData *preferencesData = [[PreferencesData alloc] init]; [preferencesData setTextSource:textSource]; [preferencesData setSendSpeed:sendSpeed];// Get the full path of our preferences data archive file NSString *prefsDataPath = [self preferencesDataPath];// Archive the preferences data to file [NSKeyedArchiver archiveRootObject:preferencesData toFile:prefsDataPath]; [preferencesData release]; }
And here’s what we do now:
-(IBAction) sourceControlIndexChanged {[[NSUserDefaults standardUserDefaults] setValue:self.sendSpeed forKey:@"sendSpeed"]; [[NSUserDefaults standardUserDefaults] setValue:self.textSource forKey:@"textSource"]; [[NSUserDefaults standardUserDefaults] synchronize]; }
So we’ve saved a lot of code and increased understandability, and all by doing it “the right way.”
What have you done “the wrong way” that you’d like to go back and do over?