【问题标题】:Settings bundle values returning nil设置捆绑值返回 nil
【发布时间】:2016-07-15 05:16:38
【问题描述】:

我在我的应用中添加了一个设置包,在 Xcode 中它出现在我的项目树视图的根目录中。

Root.plist 文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>StringsTable</key>
    <string>Root</string>
    <key>PreferenceSpecifiers</key>
    <array>
        <dict>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
            <key>Title</key>
            <string>Service</string>
        </dict>
        <dict>
            <key>Type</key>
            <string>PSTextFieldSpecifier</string>
            <key>Title</key>
            <string>Hostname</string>
            <key>Key</key>
            <string>service_hostname</string>
   <!-- and so on -->

当我在 iOS 上打开“设置”应用时,该条目会出现在底部,我可以完美地显示和编辑我的设置。

但是我无法从代码中检索这些值。这是我的代码:

static func loadSettings() {

    let ud = NSUserDefaults.standardUserDefaults()
    ud.synchronize()

    Settings.hostName = ud.stringForKey("service_hostname")
    // etc
}

我还尝试了 ud.objectForKeyud.valueForKey - 两者都返回 nil

设置Settings.hostName 后,Xcode 调试器报告它的值为nil,尽管我在“设置”应用中设置了显式值。

我看到了这个帖子 (iPhone App : How to get default value from root.plist?),其中有人发布了一段 Objective-C 代码,该代码手动将 Root.plist 文件直接加载到 NSMutableDictionary 并调用 NSUserDefaults.standardUserDefaults().registerDefaults 但这似乎是一个 hack(我无法让它在 Swift 中工作,因为编译器说 stringByAppendingPathComponent 不再存在)

为什么NSUserDefaults 没有从“设置”应用中获取设置?

【问题讨论】:

    标签: ios swift settings nsuserdefaults


    【解决方案1】:

    显然原因是如果我在plist 中的设置定义了默认值并且用户没有明确设置值,那么设置应用程序中显示的值将是plist 文件中的默认值,但是NSUserDefaults API 仍将返回 nil

    不幸的是,这意味着如果默认值有意义(例如默认 Web 服务地址 URI:“http://www.example.com”)它必须在我的项目中存在两次:作为默认值在 plist 和我的程序中代码:

    根.plist:

      <dict>
          <key>Key</key>          <string>mySettingKey</string>
          <key>Title</key>        <string>Some address</string>
          <key>Type</key>         <string>PSTextFieldSpecifier</string>
          <key>DefaultValue</key> <string>http://www.example.com</string>
          <key>IsSecure</key>     <false />
          <key>KeyboardType</key> <string>Alphabet</string>
      </dict>
    

    Program.swift:

    let ud = NSUserDefaults.standardUserDefaults()
    ud.synchronize()
    
    var mySettingValue = ud.stringForKey("mySettingKey")
    if mySettingValue == nil {
        mySettingValue = "http://www.example.com"
    }
    

    这太令人惊讶了。

    【讨论】:

    • 我想我快疯了。找了很久的解决方法!为什么会这样?有谁知道吗?
    • @Dai 这对我不起作用,它给出 nil 值.. 我错过了什么吗?
    • @mm24 “它给出了一个 nil 值” - 这就是我的帖子的内容,以及为什么解决方案是检查 nil 值并在代码中提供一个默认值。
    • @Dai 谢谢!我遇到的问题是我在 Settings.bundle 中提供了一个默认值,那么为什么我还需要以编程方式提供一个默认值呢?
    • @mm24 我不知道我们为什么要这样做,但我们必须:)
    【解决方案2】:

    可以从Settings.bundle 中获取默认值并添加到 UserDefaults。可以在AppDelegate.swift 中从didFinishLaunchingWithOptions 调用以下函数。

    func setDefaultsFromSettingsBundle() {
        //Read PreferenceSpecifiers from Root.plist in Settings.Bundle
        if let settingsURL = Bundle.main.url(forResource: "Root", withExtension: "plist", subdirectory: "Settings.bundle"),
            let settingsPlist = NSDictionary(contentsOf: settingsURL),
            let preferences = settingsPlist["PreferenceSpecifiers"] as? [NSDictionary] {
    
            for prefSpecification in preferences {
    
                if let key = prefSpecification["Key"] as? String, let value = prefSpecification["DefaultValue"] {
    
                    //If key doesn't exists in userDefaults then register it, else keep original value
                    if UserDefaults.standard.value(forKey: key) == nil {
    
                        UserDefaults.standard.set(value, forKey: key)
                        NSLog("registerDefaultsFromSettingsBundle: Set following to UserDefaults - (key: \(key), value: \(value), type: \(type(of: value)))")
                    }
                }
            }
        } else {
            NSLog("registerDefaultsFromSettingsBundle: Could not find Settings.bundle")
        }
    }
    

    【讨论】:

    • 这里有一个更好的用途。获得preferences 后,将其与UserDefaults register 一起使用。这比循环并显式设置每个值要好得多。
    • @rmaddy 在这里我检查 UserDefaults 是否已经包含特定的密钥,并且在这种情况下我没有设置。不确定在这种情况下 UserDefaults.register(:) 将如何工作
    • 您可以将整个for 循环替换为对register 的调用。而已。不需要循环。无需显式设置任何值。只需使用那一行注册默认值,您就完成了。简单得多。
    • @rmaddy 我认为我们在这里并不了解对方。如果我将for 循环替换为检查register 中是否已存在UserDefaults 中的键,则同时更改并位于UserDefaults(相同键)中的所有设置都将替换为Settings.bundle 中的默认值。如果应用程序已经安装并运行了一段时间,则可能存在密钥,用户可能已经更改了设置中的某些内容,并且应该保持不变。但是您是正确的,您的解决方案要简单得多,如果之前未安装应用程序(新应用程序),我会使用您的解决方案。
    • 没有。对register 的调用不会更改任何值。这不是它的工作原理。当您尝试在 UserDefaults 中查找值时,如果有明确设置的值,则返回该值。如果没有明确设置值,则参考注册的默认值。如果那里有一个值,则返回该默认值,否则返回nilregister 对任何现有值都没有任何影响。
    【解决方案3】:

    您应该注册您的默认设置,以便同步。 source from here

    // Swift 3
    var appDefaults = Dictionary<String, AnyObject>()
    appDefaults["mySettingKey"] = "http://www.example.com" // Default Value
    
    UserDefaults.standard.register(appDefaults)
    UserDefaults.standard.synchronize()
    
    let mySettingValue = UserDefaults.standard.string(forKey: "mySettingKey")
    

    请记住,当您的应用使用设置包时,您还应该使用 registerDefaults:。由于您已经在设置包的 plist 中指定了默认值,因此您可能希望您的应用程序自动选择这些值。然而,事实并非如此。设置包中包含的信息只能由 iOS Settings.app 读取,而不会由您的应用读取。为了让您的应用使用 Settings.app 中显示的相同默认值,您必须手动将用户默认键及其默认值复制到单独的 plist 文件中,并将其注册到默认数据库,如上所示。

    【讨论】:

    • 有趣的是,当我注册并同步我的值时,然后立即转身查询它们的值,我什么也没得到……不得不做旧式的“setObject:forKey:”。
    • 调用synchronize 在这里毫无意义。 1. 调用register 实际上并没有改变任何东西,所以没有什么要同步的。 2. 调用synchronize 从来都不需要。
    【解决方案4】:

    您需要手动从 Settings.bundle 中读取默认值并将其添加到 UserDefaults 注册域,这可以通过 UserDefaults.register() 完成。通过使用注册域,值保存在内存中(易失性),因此不需要调用UserDefaults.synchronize()

    斯威夫特 4 和 5

    这里有几行可以完成工作的 Swift 代码:

    // Register the default values from Settings.bundle
    if let settingsURL = Bundle.main.url(forResource: "Root", withExtension: "plist", subdirectory: "Settings.bundle"),
        let settingsRootDict = NSDictionary(contentsOf: settingsURL),
        let prefSpecifiers = settingsRootDict["PreferenceSpecifiers"] as? [NSDictionary],
        let keysAndValues = prefSpecifiers.map({ d in (d["Key"], d["DefaultValue"]) }) as? [(String, Any)] {
            UserDefaults.standard.register(defaults: Dictionary(uniqueKeysWithValues: keysAndValues))
    }
    

    【讨论】:

      猜你喜欢
      • 2011-06-14
      • 2014-10-11
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多