问题
I am using the InAppSettingsKit
modules to manage my settings.
I can include it in my project to the point where I can build a settings page that interacts correctly with my project. I have a root.plist that I can change and when I first run my app on the simulator I can see the changes reflected.
However, for the life of me, I cannot access the settings in NSUserDefaults
through my code. When I run the code below I can loop through the contents of the [NSUserDefaults standardUserDefaults]
. However, I only see a bunch of generic settings that are not related to the stuff in my plist file
NSUserDefaults* d = [NSUserDefaults standardUserDefaults];
NSDictionary* dict = [d dictionaryRepresentation];
NSString* descKey;
NSString* descObject;
NSString* classTypeForKey;
NSString* classTypeForObject;
id myObject;
for (id key in dict) {
myObject = [dict objectForKey:key];
classTypeForObject = [[myObject class] description];
classTypeForKey = [[key class] description];
descKey = [key description];
descObject = [myObject description];
}
So where does InAppSettingsKit put the settings? The doc says in [NSUserDefaults standardUserDefaults]
so I am at a loss.
回答1:
I think your problem relies on a misunderstanding of the concept. Root.plist doesn't store any settings but just defines how Settings.app and InAppSettingsKit work on your userDefaults.
When your app launches the first time, there are no userDefaults. Usually, you start by setting appropriate default settings by reading in a static userDefaults.plist
from your Resources:
// Load the default values for the user defaults
NSString* pathToUserDefaultsValues = [[NSBundle mainBundle]
pathForResource:@"userDefaults"
ofType:@"plist"];
NSDictionary* userDefaultsValues = [NSDictionary dictionaryWithContentsOfFile:pathToUserDefaultsValues];
// Set them in the standard user defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValues];
回答2:
Here is Ojas's code modified a bit further to account for ARC.
You would probably call initializeSettings from your AppDelegate didFinishLaunchingWithOptions.
#import "IASKSettingsReader.h"
...
- (void)initializeSettings
{
// standard stored preference values
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
// settings files to process
NSMutableArray *preferenceFiles = [[NSMutableArray alloc] init];
// begin with Root file
[preferenceFiles addObject:@"Root"];
// as other settings files are discovered will be added to preferencesFiles
while ([preferenceFiles count] > 0) {
// init IASKSettingsReader for current settings file
NSString *file = [preferenceFiles lastObject];
[preferenceFiles removeLastObject];
IASKSettingsReader *settingsReader = [[IASKSettingsReader alloc]
initWithFile:file];
// extract preference specifiers
NSArray *preferenceSpecifiers = [[settingsReader settingsBundle]
objectForKey:kIASKPreferenceSpecifiers];
// process each specifier in the current settings file
for (NSDictionary *specifier in preferenceSpecifiers) {
// get type of current specifier
NSString *type = [specifier objectForKey:kIASKType];
// need to check child pane specifier for additional file
if ([type isEqualToString:kIASKPSChildPaneSpecifier]) {
[preferenceFiles addObject:[specifier objectForKey:kIASKFile]];
}
else {
// check if current specifier has a default value
id defaultValue = [specifier objectForKey:kIASKDefaultValue];
if (defaultValue) {
// get key from specifier and current stored preference value
NSString *key = [specifier objectForKey:kIASKKey];
id value = [defaults objectForKey:key];
// update preference value with default value if necessary
if (key && value == nil) {
[defaults setObject:defaultValue forKey:key];
}
}
}
}
}
// synchronize stored preference values
[defaults synchronize];
}
回答3:
It's all making sense now. I had tried to register the defaults in previous attempts to get things working, but I thought I could do this using the Root.plist file. But of course the registerDefaults methods just sees the high level entries of StringsTable and PreferenceSpecifiers. Thanks for pointing this out Ortwin.
So, instead of building a separate userDefault.plist file, I found the following code that parses the root.plist file to manually add the defaults to NSUserDefaults. I can't remember where I got it, but it seems to work well. If there are reasons why I shouldn't do it this way, feel free to elaborate:
- (void)setDefaults {
//get the plist location from the settings bundle
NSString *settingsPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Settings.bundle"];
NSString *plistPath = [settingsPath stringByAppendingPathComponent:@"Root.plist"];
//get the preference specifiers array which contains the settings
NSDictionary *settingsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSArray *preferencesArray = [settingsDictionary objectForKey:@"PreferenceSpecifiers"];
//use the shared defaults object
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//for each preference item, set its default if there is no value set
for(NSDictionary *item in preferencesArray) {
//get the item key, if there is no key then we can skip it
NSString *key = [item objectForKey:@"Key"];
if (key) {
//check to see if the value and default value are set
//if a default value exists and the value is not set, use the default
id value = [defaults objectForKey:key];
id defaultValue = [item objectForKey:@"DefaultValue"];
if(defaultValue && !value) {
[defaults setObject:defaultValue forKey:key];
}
}
}
//write the changes to disk
[defaults synchronize];
}
}
回答4:
I modified Sam's code to process child panes specified in separate files:
#import "IASKSettingsReader.h"
- (void)initializeSettings
{
// standard stored preference values
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
// settings files to process
NSMutableArray *preferenceFiles = [[NSMutableArray alloc] init];
// begin with Root file
[preferenceFiles addObject:@"Root"];
// as other settings files are discovered will be added to preferencesFiles
while (![preferenceFiles isEmpty]) {
// init IASKSettingsReader for current settings file
NSString *file = [[preferenceFiles lastObject] retain];
[preferenceFiles removeLastObject];
IASKSettingsReader *settingsReader = [[IASKSettingsReader alloc]
initWithFile:file];
[file release];
// extract preference specifiers
NSArray *preferenceSpecifiers = [[settingsReader settingsBundle]
objectForKey:kIASKPreferenceSpecifiers];
// process each specifier in the current settings file
for (NSDictionary *specifier in preferenceSpecifiers) {
// get type of current specifier
NSString *type = [specifier objectForKey:kIASKType];
// need to check child pane specifier for additional file
if ([type isEqualToString:kIASKPSChildPaneSpecifier]) {
[preferenceFiles addObject:[specifier objectForKey:kIASKFile]];
}
else {
// check if current specifier has a default value
id defaultValue = [specifier objectForKey:kIASKDefaultValue];
if (defaultValue) {
// get key from specifier and current stored preference value
NSString *key = [specifier objectForKey:kIASKKey];
id value = [defaults objectForKey:key];
// update preference value with default value if necessary
if (key && value == nil) {
[defaults setObject:defaultValue forKey:key];
}
}
}
}
[settingsReader release];
}
[preferenceFiles release];
// synchornize stored preference values
[defaults synchronize];
}
回答5:
I have based my answer on the one given by @Ojas. But I converted it to Swift. I hope this is useful for people using Swift
func processDefaultSettings() {
let defaults = IASKSettingsStoreUserDefaults().defaults
// settings files to process
var preferenceFiles = [String]()
// begin with Root file
preferenceFiles.append("Root")
// as other settings files are discovered will be added to preferencesFiles
while !preferenceFiles.isEmpty {
// init IASKSettingsReader for current settings file
let file = preferenceFiles.last
let settingsReader = IASKSettingsReader(file: file)
preferenceFiles.removeLast()
// extract preference specifiers
if let preferenceSpecfiers = settingsReader.settingsDictionary[kIASKPreferenceSpecifiers] as? NSArray {
// process each specifier in the current settings file
for specifier in preferenceSpecfiers {
// get type of current specifier
let type = specifier[kIASKType] as! String
// need to check child pane specifier for additional file
if type == kIASKPSChildPaneSpecifier {
preferenceFiles.append(specifier[kIASKFile] as! String)
}
else {
// check if current specifier has a default value
if let defaultValue = specifier[kIASKDefaultValue] as AnyObject? {
// get key from specifier and current stored preference value
if let key = specifier[kIASKKey] as? String {
if let value = defaults.objectForKey(key) {
// Nothing to do - already set
}
else {
let appDefaults = [key: defaultValue]
defaults.registerDefaults(appDefaults)
}
}
}
}
}
}
// synchornize stored preference values
defaults.synchronize()
}
}
回答6:
I amended Steve's answer to his own question above and the following seems to work for me:
In your IASKAppSettingsViewController subclass-
- (void)viewDidLoad
{
[super viewDidLoad];
NSDictionary* settingsBundle = [self.settingsReader settingsBundle];
NSArray *preferencesArray = [settingsBundle objectForKey:@"PreferenceSpecifiers"];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
//for each preference item, set its default if there is no value set
for(NSDictionary *item in preferencesArray) {
//get the item key, if there is no key then we can skip it
NSString *key = [item objectForKey:@"Key"];
if (key) {
//check to see if the value and default value are set
//if a default value exists and the value is not set, use the default
id value = [defaults objectForKey:key];
id defaultValue = [item objectForKey:@"DefaultValue"];
if(defaultValue && !value) {
[defaults setObject:defaultValue forKey:key];
}
}
}
//write the changes to disk
[defaults synchronize];
}
来源:https://stackoverflow.com/questions/6667911/having-problems-reading-settings-while-using-inappsettingskit