iOS: Yay, In-Application Preferences
There are lots of nice resources (both printed and online) regarding the use of the Settings.bundle
to store the user’s preferences on the iPhone, but there seems to be a severe lack of resources about not using it.
Settings.bundle
is admittedly pretty cool. With a single .plist
file you can create a settings management page in your application that covers a number of common requirements. But besides the obvious UI concern of storing your application’s preferences in a location many of your users will never check, the bigger problem is one of limitations; if the Settings.bundle
does what you need, great. If it doesn’t, get the hell out—you can’t put any custom code into the Settings app.
Need to make a query to determine the options for a setting? Not happening. Multi-line text entry? Sorry.
With Raconteur I happily used Settings.bundle
in version 1.0 and then hit a brick wall when adding password protection because there’s no way to do password confirmation in the Settings app. And surely I’m not going to leave my application’s settings arbitrarily (from a user’s perspective) split between inside and outside the application, so I have to backtrack and move everything in.
My advice for developers starting on new applications is don’t use Settings.bundle
. Unless you know all of your requirements upfront and they couldn’t possibly change (and if you do, I’d like to visit your mythical waterfall). On the whole, it’s exceedingly anti-agile to hit a brick wall and have to redo work. Not to mention breaking user expectations for everyone who had already found the external settings.
Now, if you’re not using the Settings.bundle
mechanism, how do you store your user’s preferences? I imagine this is exceeding obvious to experienced Cocoa programmers, but to developers starting with the iPhone, the docs aren’t so clear… the NSUserDefaults
mechanism works the same whether you are using Settings.bundle
or not. Just grab the defaults object, register some default defaults (…), and start using them:
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
@"Cheesy Ramen", @"favoriteFood",
@"YES", @"donateToHungryPandas",
nil]];
Note that this registerDefaults
call is important whether you use Settings.bundle
or not. You might be shocked (I know I was) to find that your application’s settings (and associated defaults) from your Settings.bundle
might not be programmatically available until the user runs the Settings app. Which means that you probably set the defaults once in the Settings.bundle
and now you get to set them again in code. I know, lame.
Access your settings like so:
if ([[defaults objectForKey:@"favoriteFood"] isEqualToString:@"Cheesy Ramen"]) {
// Screw cheesy ramen...
[defaults setObject:@"Pizza" forKey:@"favoriteFood"];
}
A few more tips:
- Definitely consider defining some constants for your preference keys so that the compiler can find your typos instead of your users finding them.
- Since
NSUserDefaults
acts like anNSMutableDictionary
, it only stores objects (not yourbool
s,int
s, etc.). Fortunately it provides nice convenience methods likeboolForKey:
andsetBool:forKey:
to abstract away conversions to object types (like booleanYES
to@"YES"
). - You can use both
Settings.bundle
and your own interface to access the exact same preference data with no problems (makes sense but I’m just pointing it out). That said, it probably doesn’t make a great deal of sense from a UI perspective. NSUserDefaults
is automagically saved off at regular intervals (and, I think, when you exit the application). You don’t have to worry about saving those preferences, or about exactly where they go. That’s why it’s automagic.- Once you remove
Settings.bundle
from the program, it may still appear to be around, showing up in theSettings
app. In reality it won’t hang around on your users’ devices (I believe), but to remove it from your test devices, simply make sure to runBuild
>Clean All Targets
in Xcode.