Register default settings from the Settings.bundle

2019-01-28 11:06发布

I'm adding a Settings.bundle file to my iOS application. It's very minimal with one or two settings. The plist file in the Settings.bundle specifies the defaults, to the Settings application. I've read that to make my iOS application aware of these defaults, I have to also code them into my application. This seems like repeating myself and leaving an opening for defaults to easily get out of sync as I modify the program.

I know I can register the defaults using the contents of plist file, like so:

[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Defaults" ofType:@"plist"]]];

That also seems like repeating myself, though. Would it be possible to use the plist file from the Settings.bundle as the source of these defaults, so that I only specify defaults in 1 location?

I trued adjusting that load to look something like this:

[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Settings.bundle/Root" ofType:@"plist"]]];

That did not work, though. Does anybody know if this is possible, and how to load that plist file?

4条回答
够拽才男人
2楼-- · 2019-01-28 11:44

That is not necessary, you can simply call the data in the plist by calling the key that it was assigned to in the plist, for example,

_buttonSelection = [[NSUserDefaults standardUserDefaults] stringForKey:@"buttonAction01"]; //_buttonSelection is a NSString so that can be anything
查看更多
看我几分像从前
3楼-- · 2019-01-28 11:49

Yes, it is possible. Here's how you would do it in Swift:

func loadDefaults() {

    let pathStr = NSBundle.mainBundle().bundlePath
    let settingsBundlePath = pathStr.stringByAppendingPathComponent("Settings.bundle")
    let finalPath = settingsBundlePath.stringByAppendingPathComponent("Root.plist")
    let settingsDict = NSDictionary(contentsOfFile: finalPath)
    let prefSpecifierArray = settingsDict?.objectForKey("PreferenceSpecifiers") as NSArray

    var defaults:[NSObject:AnyObject] = [:]

    for prefItem in prefSpecifierArray {

        if let key = prefItem.objectForKey("Key") as String! {

            let defaultValue:AnyObject? = prefItem.objectForKey("DefaultValue")
            defaults[key] = defaultValue
        }
    }

    _userDefaults.registerDefaults(defaults)
}

That will load the DefaultValue from each PreferenceSpecifier with a Key. For example:

<dict>
    <key>Type</key>
    <string>PSMultiValueSpecifier</string>
    <key>Title</key>
    <string>Frequency</string>
    <key>Key</key>
    <string>frequency</string>
    <key>DefaultValue</key>
    <integer>15</integer>
    <key>Titles</key>
    <array>
        <string>5 minutes</string>
        <string>15 minutes</string>
        <string>30 minutes</string>
        <string>60 minutes</string>
    </array>
    <key>Values</key>
    <array>
        <integer>5</integer>
        <integer>15</integer>
        <integer>30</integer>
        <integer>60</integer>
    </array>
</dict>

The loadDefaults function will load the default value of 15 into the user default for "frequency".

I recommend you call the loadDefaults function your app delegate, from the didFinishLaunchingWithOptions handler, like this:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    loadDefaults()

    return true
}

Then later in your app, say in a UITableViewController, you can load the user default value like this:

let kFrequency = "frequency"
frequency = _userDefaults.integerForKey(kFrequency)
查看更多
Anthone
4楼-- · 2019-01-28 11:53

Based on Andy's answer above, adapted for Swift 4, and avoiding the use of force-unwrap/force-cast:

private func loadDefaults() {
    let userDefaults = UserDefaults.standard

    let pathStr = Bundle.main.bundlePath
    let settingsBundlePath = (pathStr as NSString).appendingPathComponent("Settings.bundle")
    let finalPath = (settingsBundlePath as NSString).appendingPathComponent("Root.plist")
    let settingsDict = NSDictionary(contentsOfFile: finalPath)
    guard let prefSpecifierArray = settingsDict?.object(forKey: "PreferenceSpecifiers") as? [[String: Any]] else {
        return
    }

    var defaults = [String: Any]()

    for prefItem in prefSpecifierArray {
        guard let key = prefItem["Key"] as? String else {
            continue
        }
        defaults[key] = prefItem["DefaultValue"]
    }
    userDefaults.register(defaults: defaults)
}
查看更多
够拽才男人
5楼-- · 2019-01-28 12:01

Wanted to add this as a comment to Andy's answer, but Stack Overflow didn't let me (too long for a comment).
If you're using ObjC here's your version:

-(void)registerDefaults
{
    NSString* pathString = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Settings.bundle"];
    NSString* rootPath = [pathString stringByAppendingPathComponent:@"Root.plist"];
    NSDictionary* settingsDict = [NSDictionary dictionaryWithContentsOfFile:rootPath];
    NSArray* prefSpecifiers = settingsDict[@"PreferenceSpecifiers"] ;

    NSMutableDictionary* defaults = [NSMutableDictionary dictionary];

    for (NSDictionary* item in prefSpecifiers) {
        NSString* theKey = item[@"Key"];
        NSObject* defaultValue = item[@"DefaultValue"];

        if (defaultValue && theKey) {
            defaults[theKey] = defaultValue;
        }

    }

    if (defaults.count > 0) {
        [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
    }

}
查看更多
登录 后发表回答