Implementing preferences in iOS – the right way

Posted in Computer Science, Morse Trainer on August 25th, 2013 by michael

Douglas_Groce_CorriganA few weeks ago, I shared how I implemented preferences in my Morse Trainer appthe 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?

Photograph of Douglas “Wrong Way” Corrigan courtesy of Wikipedia and sourced from the U.S. Government; as such, it is in the public domain.  I highly recommend the very entertaining Wikipedia article.