Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Settings bundle values returning nil

I added a Settings bundle to my app and in Xcode it appears in the root of my project tree view.

The Root.plist file looks like this:

<?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 -->

When I open the Settings app on iOS the entry appears at the bottom and I can display and edit my settings perfectly fine.

However I cannot retrieve these values from code. Here's my code:

static func loadSettings() {

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

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

I also tried ud.objectForKey and ud.valueForKey - both return nil as well.

After setting Settings.hostName the Xcode debugger reports it has a value of nil despite me setting an explicit value in the Settings app.

I saw this thread ( iPhone App : How to get default value from root.plist? ) where someone posted a chunk of Objective-C code that manually loads the Root.plist file directly into an NSMutableDictionary and calls NSUserDefaults.standardUserDefaults().registerDefaults but that seems like a hack (and I can't get it to work in Swift because the compiler says that stringByAppendingPathComponent doesn't exist anymore)

Why isn't NSUserDefaults picking up the settings from the Settings app?

like image 627
Dai Avatar asked Mar 27 '16 23:03

Dai


2 Answers

Apparently the cause is that if my settings in the plist have defaults defined and the user has not explicitly set a value, then the value displayed in the Settings app will be the defaults from the plist file, however the NSUserDefaults API will still return nil.

Unfortunately this means that if the default value is meaningful (such as a default web-service address URI: "http://www.example.com") it must exist twice in my project: as a default in the plist and in my program code:

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

That's surprising.

like image 175
Dai Avatar answered Oct 31 '22 18:10

Dai


Default values can be taken from Settings.bundle and added to UserDefaults. Following function can be called in AppDelegate.swift from 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")
    }
}
like image 11
Najdan Tomić Avatar answered Oct 31 '22 18:10

Najdan Tomić