设置包值返回 nil

Settings bundle values returning nil

我在我的应用程序中添加了一个设置包,并且在 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 - 都 return 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 不从“设置”应用中选取设置?

显然是因为如果我在 plist 中的设置定义了默认值并且用户没有明确设置值,那么“设置”应用中显示的值将是 [=12] 中的默认值=] 文件,但是 NSUserDefaults API 仍然 return nil.

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

Root.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"
}

真令人惊讶。

您应该注册您的默认设置,以便同步。 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 文件中,并将其注册到默认数据库,如上所示。

可以从 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")
    }
}

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

Swift 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))
}