Registering a plist to NSUserDefaults I've been struggling with registering a plist into NSUserDefaults and I've finally got it working, though I'm not sure that its written properly. I'm hoping someone here can shine some light on this process. 

My intention with this code:

I want to be able to set all of my values in a plist and essentially use that to "seed" my NSUserDefaults. My end goal is to use this as a user settings menu.

My question:

What is the correct way to register a plist into the NSUserDefaults?

I followed the example from this this post and I received compiler errors. So I started experimenting. I could only get the code from the post to work if I added setObject(dict, forKey: "defaults") first.

Once added, it seemed to render the registerDefaults line pointless as the code runs with or without it.

The lines that I am questioning are these:

userDefaults.setObject(dict, forKey: "defaults") // without this my code doesn't work

userDefaults.registerDefaults(dict as! [NSObject : AnyObject])  // with or without this code works... do I need this?


I've placed the entirety of the code below to put above lines in context. This is all of the code so you can copy it right into Xcode and it will work.

ViewController.swift

class ViewController: UIViewController {

    let userDefaults = NSUserDefaults.standardUserDefaults()

    override func viewDidLoad() {
        super.viewDidLoad()

        let prefs = NSBundle.mainBundle().pathForResource("Settings", ofType: "plist")
        let dict = NSDictionary(contentsOfFile: prefs!)
        userDefaults.setObject(dict, forKey: "defaults")                // without this code doesn't work
        userDefaults.registerDefaults(dict as! [NSObject : AnyObject])  // with or without this code works... do I need this?
        userDefaults.synchronize()

        // this just toggles the aBool value
        if let defaults = userDefaults.valueForKey("aBool") as? NSNumber {

            if defaults as NSObject == 1 {
                println("inside the conditional: \(defaults)")
                userDefaults.setValue(0, forKey: "aBool")
                userDefaults.synchronize()
                println("Setting aBool to true")
            } else {
                userDefaults.setValue(1, forKey: "aBool")
                userDefaults.synchronize()
                println("setting aBool to false")
            }
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }
}


Settings.plist
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}



If all your method does is call up to the super class's implementation of the method, you can safely omit the method entirely.  I understand this is boilerplate code that Xcode automatically inserts any time you create a new view controller, but at the end of the day, it's just clutter that should be cleaned up.




override func viewDidLoad() {
    super.viewDidLoad()

    let prefs = NSBundle.mainBundle().pathForResource("Settings", ofType: "plist")
    let dict = NSDictionary(contentsOfFile: prefs!)
    userDefaults.setObject(dict, forKey: "defaults")                // without this code doesn't work
    userDefaults.registerDefaults(dict as! [NSObject : AnyObject])  // with or without this code works... do I need this?
    userDefaults.synchronize()

    // this just toggles the aBool value
    if let defaults = userDefaults.valueForKey("aBool") as? NSNumber {
       if defaults as NSObject == 1 {
            println("inside the conditional: \(defaults)")
            userDefaults.setValue(0, forKey: "aBool")
            userDefaults.synchronize()
            println("Setting aBool to true")
        } else {
            userDefaults.setValue(1, forKey: "aBool")
            userDefaults.synchronize()
            println("setting aBool to false")
        }
    }
}



I don't want to say this is objectively too much code for viewDidLoad, but it's not appropriate for this to all go in viewDidLoad.  This really doesn't belong in the viewDidLoad of any view controller, really.  This is code we seem to only want to load once.  This really belongs in our app delegate, in application(_:didFinishLaunchingWithOptions:).

Once we get our list loaded into NSUserDefaults, we just need to access the values through NSUserDefaults, which is a singleton we can easily access from anywhere.

As a final note about all of this, calling synchronize is generally going to be unnecessary.  NSUserDefaults will synchronize itself when it finds a convenient time.  Moreover, all synchronize does is synchronize what NSUserDefaults has stored in memory with what it has stored in long-term storage.  The only time you can lose something that you put in NSUserDefaults is if your app crashes after you changed a value and before NSUserDefaults had a chance to synchronize.  This is extraordinarily rare.  And for emphasis, realize that I said this happens when your app crashes.  If the user exits your app, NSUserDefaults will synchronize before the app is allowed to completely exit.

And when you access the values in NSUserDefaults, you'll get the value it was most recently set to (which isn't necessarily what is saved to the permanent storage at that moment).

Calling synchronize just forces the synchronization to happen at that very instant, which is entirely unnecessary.



And, for the record, our inner most conditional can be simplified:

if let defaults = userDefaults.valueForKey("aBool") as? NSNumber {
    userDefaults.setValue(!defaults.boolValue, forKey: "aBool")
}