MJQZ1347
MJQZ1347

Reputation: 2687

Returning the value of a settings bundle switch in Swift / Xcode 6

I have difficulties returning the value of a stored boolean key.

My root.plist looks like this:

enter image description here

I've got this in AppDelegate:

    let a = NSUserDefaults.standardUserDefaults().boolForKey("sound")

    if a { // This is where it breaks
        println("sound is on")
    }

Any ideas what I'm doing wrong?

Upvotes: 1

Views: 9818

Answers (3)

atomoil
atomoil

Reputation: 404

I came this post as I had the same issue today, and wasn't very satisfied with this note:

In order to have your app use the same defaults as shown inside the Settings.app, you have to manually copy the user defaults keys and their default values into a separate plist file and register it with the defaults database as shown above.

I thought there must be a more DRY solution, so I made the following extension. It iterates through the Settings.bundle's plists (including Child plists) and registers any DefaultValues it finds. Also available in this gist.

extension UserDefaults {

    static func syncSettingsBundle() {
        if let bundlePath = Bundle.main.path(forResource: "Settings", ofType: "bundle"),
            let plistPath = URL(string: bundlePath)?.appendingPathComponent("Root.plist").absoluteString {

            let defaultDefaults = UserDefaults.defaultsForPlist(path: plistPath, defaults: [:])
            UserDefaults.standard.register(defaults: defaultDefaults)
        }
    }

    static private func defaultsForPlist(path: String, defaults: [String: Any]) -> [String: Any] {
        var mutableDefaults = defaults
        if let plistXML = FileManager.default.contents(atPath: path) {
            do {
                let plistData = try PropertyListSerialization.propertyList(from: plistXML,
                                                                           options: .mutableContainersAndLeaves,
                                                                           format: nil) as! [String:AnyObject]
                if let prefs = plistData["PreferenceSpecifiers"] as? Array<[String: Any]> {
                    prefs.forEach { (pref: [String : Any]) in
                        if let key = pref["Key"] as? String,
                            let value = pref["DefaultValue"] {
                            mutableDefaults[key] = value
                        } else if let type = pref["Type"] as? String,
                            type == "PSChildPaneSpecifier",
                            let file = pref["File"] as? String,
                            let childPath = URL(string: path)?
                                .deletingLastPathComponent()
                                .appendingPathComponent(file)
                                .absoluteString {
                            mutableDefaults = UserDefaults.defaultsForPlist(path: childPath, defaults: mutableDefaults)
                        }
                    }
                } else {
                    print("Error no PreferenceSpecifiers in \(path)")
                }
            } catch {
                print("Error reading plist: \(error) in \(path)")
            }
        } else {
            print("No plist found found at \(path)")
        }
        return mutableDefaults
    }
}

Upvotes: 0

Mark McCorkle
Mark McCorkle

Reputation: 9414

First you should register your defaults so everything is sync'd up and defaults are assigned. Then you'll know a value exists. These values are not automatically synchronized on startup.

var appDefaults = Dictionary<String, AnyObject>()
appDefaults["sound"] = false
NSUserDefaults.standardUserDefaults().registerDefaults(appDefaults)
NSUserDefaults.standardUserDefaults().synchronize()

let a = NSUserDefaults.standardUserDefaults().boolForKey("sound")

if a { // This is where it breaks
    println("sound is on")
}

Below is an excerpt from a relevant article.

When Your App Uses a Settings Bundle

Keep in mind that you should also use registerDefaults: when your app uses a Settings Bundle. Since you already specified default values inside the settings bundle’s plist, you may expect that your app picks these up automatically. However, that is not the case. The information contained in the settings bundle is only read by the iOS Settings.app and never by your app. In order to have your app use the same defaults as shown inside the Settings.app, you have to manually copy the user defaults keys and their default values into a separate plist file and register it with the defaults database as shown above.

And HERE is the relevant discussion directly from Apple.

Registering Defaults

Discussion

If there is no registration domain, one is created using the specified dictionary, and NSRegistrationDomain is added to the end of the search list. The contents of the registration domain are not written to disk; you need to call this method each time your application starts. You can place a plist file in the application's Resources directory and call registerDefaults: with the contents that you read in from that file.

Upvotes: 14

Hugo Alonso
Hugo Alonso

Reputation: 6824

Use this:

    var myDict: NSDictionary?
// Read from the Configuration plist the data to make the state of the object valid.
    if let path = NSBundle.mainBundle().pathForResource("root", ofType: "plist") {
        myDict = NSDictionary(contentsOfFile: path)
    }

    // Set the values to init the object
    if let dict = myDict {
        if let a = dict["sound"] as? Bool {
         if a { 
            println("sound is on")
          }
        }
    }

Upvotes: 1

Related Questions