如何在 Objective-c/Swift 中从 settings.bundle 中检索值?

22

我创建了一个从 settings.bundle 中设置和获取值的项目。我还在 settings.bundle 文件中设置了一些默认值。现在的问题是当我检索值时,

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
loginName.text = [defaults objectForKey:@"login_name"];

第一次显示为 null,但该值在 iPhone 应用程序设置中设置了。如果我更改该值或手动设置,则可以正确检索该值。

请帮助我解决。

6个回答

38

虽然您定义了默认设置,但它们实际上并没有以值的形式存储。它们作为默认值存储。如果您尝试读取它,该值将为空。默认设置是另一个属性,而不是值。但这并不意味着会将默认值写入默认设置。

我所做的是首先检查某些设置是否(我确定应具有值)存储了任何内容。如果没有任何内容,则我将写入所有默认设置。

这里是一个例子。

在AppDelegate.m中,我检查 email_notifications_preference 是否具有值,如果没有,则将所有默认设置写入每个设置。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    NSUserDefaults * standardUserDefaults = [NSUserDefaults standardUserDefaults];
    NSString * email_notificaciones_preference = [standardUserDefaults objectForKey:@"email_notificaciones_preference"];
    if (!email_notificaciones_preference) {
        [self registerDefaultsFromSettingsBundle];
    }

}

这个函数是我用来将默认值写入每个元素的。

#pragma NSUserDefaults
- (void)registerDefaultsFromSettingsBundle {
    // this function writes default settings as settings
    NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
    if(!settingsBundle) {
        NSLog(@"Could not find Settings.bundle");
        return;
    }

    NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
    NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];

    NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
    for(NSDictionary *prefSpecification in preferences) {
        NSString *key = [prefSpecification objectForKey:@"Key"];
        if(key) {
            [defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
            NSLog(@"writing as default %@ to the key %@",[prefSpecification objectForKey:@"DefaultValue"],key);
        }
    }

    [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];

}
希望能帮到你。

感谢您节省了大量时间! - B.S.
非常好的解决方案 - 我将其翻译为Swift并添加为答案,以便需要的人使用。 - Christoph Sonntag
[[NSUserDefaults standardUserDefaults] synchronize]; 应该也要添加吗? - Zigii Wong
我在获取数据之前使用[[NSUserDefaults standardUserDefaults] synchronize],而不是之后。 - MiQUEL

5

更新至Swift 3:

func registerDefaultsFromSettingsBundle() {

    let userDefaults = UserDefaults.standard

    if let settingsURL = Bundle.main.url(forResource: "Root", withExtension: "plist", subdirectory: "Settings.bundle"),
        let settings = NSDictionary(contentsOf: settingsURL),
        let preferences = settings["PreferenceSpecifiers"] as? [NSDictionary] {

        var defaultsToRegister = [String: AnyObject]()
        for prefSpecification in preferences {
            if let key = prefSpecification["Key"] as? String,
                let value = prefSpecification["DefaultValue"] {

                defaultsToRegister[key] = value as AnyObject
                debugPrint("registerDefaultsFromSettingsBundle: (\(key), \(value)) \(type(of: value))")
            }
        }

        userDefaults.register(defaults: defaultsToRegister)

    } else {
        debugPrint("registerDefaultsFromSettingsBundle: Could not find Settings.bundle")
    }
}

5

如果有人需要 - 我将MIQUEL的答案翻译成了Swift(我已经尽力了,因为我还在学习):

var standardUserDefaults = NSUserDefaults.standardUserDefaults()
var us: AnyObject? = standardUserDefaults.objectForKey("your_preference")
if us==nil {
    self.registerDefaultsFromSettingsBundle();
}

并且函数registerDefaultsFromSettingsBundle:

func registerDefaultsFromSettingsBundle() {
    // this function writes default settings as settings
    var settingsBundle = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")
    if settingsBundle == nil {
        NSLog("Could not find Settings.bundle");
        return
    }
    var settings = NSDictionary(contentsOfFile:settingsBundle!.stringByAppendingPathComponent("Root.plist"))!
    var preferences: [NSDictionary] = settings.objectForKey("PreferenceSpecifiers") as [NSDictionary];
    var defaultsToRegister = NSMutableDictionary(capacity:(preferences.count));

    for prefSpecification:NSDictionary in preferences {
        var key: NSCopying? = prefSpecification.objectForKey("Key") as NSCopying?
        if key != nil {
            defaultsToRegister.setObject(prefSpecification.objectForKey("DefaultValue")!, forKey: key!)
        }
    }
    NSUserDefaults.standardUserDefaults().registerDefaults(defaultsToRegister);  
}

3

Swift 2.1的更新版本:

 func registerDefaultsFromSettingsBundle() {
    let userDefaults = NSUserDefaults.standardUserDefaults()

    if let settingsURL = NSBundle.mainBundle().URLForResource("Root", withExtension: "plist", subdirectory: "Settings.bundle"),
           settings = NSDictionary(contentsOfURL: settingsURL),
           preferences = settings["PreferenceSpecifiers"] as? [NSDictionary] {

        var defaultsToRegister = [String: AnyObject]()
        for prefSpecification in preferences {
            if let key = prefSpecification["Key"] as? String,
                   value = prefSpecification["DefaultValue"] {

                defaultsToRegister[key] = value
                NSLog("registerDefaultsFromSettingsBundle: (\(key), \(value)) \(value.dynamicType)")
            }
        }

        userDefaults.registerDefaults(defaultsToRegister);
    } else {
        NSLog("registerDefaultsFromSettingsBundle: Could not find Settings.bundle");
    }
}

有关于 Swift 3 的任何更新吗? - Jonny

3
你可以像这样使用简单的属性包装器:

用法

@SettingsBundleStorage(key: "storageUsage_preference")
var storageUsage: Double

注意,只需在变量前添加@objc,就可以使之与100%的Objective-C兼容。


代码实现:

SettingsBundle值存储在UserDefaults中,因此您可以为其使用自定义的PropertyWrapper。以下包装器将适用于任何UserDefault值,包括SettingsBundle的所有值。

属性包装器

@propertyWrapper
public struct SettingsBundleStorage<T> {    
    private let key: String

    public init(key: String) {
        self.key = key
        setBundleDefaults(plist: .root) // This is the main plist
        setBundleDefaults(plist: .child(name: "DeveloperOptions")) // This is an example child.
    }

    public var wrappedValue: T {
        get { UserDefaults.standard.value(forKey: key) as! T }
        set { UserDefaults.standard.set(newValue, forKey: key) }
    }
}

根节点和子节点

您需要为根节点和子节点的plist传递以下枚举:

extension SettingsBundleStorage {

    enum PList {
        case root
        case child(name: String)

        var name: String {
            var file: String
            switch self {
            case .root: file = "Root"
            case .child(let name): file = name.replacingOccurrences(of: ".plist", with: "")
            }
            file.append(".plist")
            return file
        }
    }
}

查找并设置默认值。

该包装器使用此函数查找捆绑密钥的默认值:

extension SettingsBundleStorage {

    func setBundleDefaults(plist: PList = .root) {
        let settingsName                    = "Settings"
        let settingsExtension               = "bundle"
        let settingsPreferencesItems        = "PreferenceSpecifiers"
        let settingsPreferenceKey           = "Key"
        let settingsPreferenceDefaultValue  = "DefaultValue"

        guard let settingsBundleURL = Bundle.main.url(forResource: settingsName, withExtension: settingsExtension),
              let settingsData = try? Data(contentsOf: settingsBundleURL.appendingPathComponent(plist.name)),
              let settingsPlist = try? PropertyListSerialization.propertyList(
                from: settingsData,
                options: [],
                format: nil) as? [String: Any],
              let settingsPreferences = settingsPlist?[settingsPreferencesItems] as? [[String: Any]] else {
            return assertionFailure("Can not get the \(plist.name) from the bundle: \(settingsName)")
        }

        var defaultsToRegister = [String: Any]()

        settingsPreferences.forEach { preference in
            if let key = preference[settingsPreferenceKey] as? String {
                defaultsToRegister[key] = preference[settingsPreferenceDefaultValue]
            }
        }

        UserDefaults.standard.register(defaults: defaultsToRegister)
    }
}

这个包装器可以将任何可编码的内容存储/恢复到用户默认设置中,包括所有已符合可编码协议的 Swift 标准数据类型。


另外,您可以找到一种相似但代码更少的方式来访问任何用户默认设置中的键值,您可以查看这里的答案


1
这是一个非常出色的答案!从现在开始,这应该是使用属性包装器的方法。 - sachadso
这个非常好用!它甚至可以在不重启应用程序的情况下自动反映设置的更改。如果您只使用单个“Settings.bundle”,则可以安全地删除“DeveloperOptions”行。 - Ignis Incendio

0

试试这个

注册设置包的默认值

NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:defaultValue forKey:@"key"];
 [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];

在检索设置包值之前同步数据

[[NSUserDefaults standardUserDefaults] synchronize]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接