Reputation: 3961
I am working on a settings view for a basic app. Basic, in there is just one switch in the settings view for the user. The switch setting is saved with NSUserDefault. I use a delegate to send the switch signal from the settings view to the main view. The delegation works properly.
The UI is basic. On the main view, a label will read On in green (if the switch is on) and Off in red (if the switch is off.) There is a setting button in the top right that will segue (settingsSegue) to the settings UITableViewController, where the UISwitch is located.
The problem is loading up the NSUserDefault once the app loads. In viewDidLoad, I check to see if there's a value saved for the switch key. If there is, load it up. If not, set it to false (in the storyboard, the switch is set to false as default.) The Switch Status loads up as Off every time. Even if the default value is On. This shouldn't be happening.
import UIKit
var nsDefaults = NSUserDefaults.standardUserDefaults()
class ViewController: UIViewController, SettingsViewControllerDelegate {
var onFromMain = Bool()
@IBOutlet weak var switchStateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let mySavedKey = nsDefaults.objectForKey("savedSwitchSettingDefault") {
// A value exists. Load it up.
nsDefaults.objectForKey("savedSwitchSettingDefault")
print("The switch is set! \(mySavedKey)")
checkSwitchState()
}
else {
// Nothing stored in NSUserDefaults yet. Set switch to False.
nsDefaults.setBool(false, forKey: "savedSwitchSettingDefault")
checkSwitchState()
}
}
func myVCDidFinish(controller: SettingsViewController, switchState: Bool) {
onFromMain = switchState.boolValue
checkSwitchState()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "settingsSegue" {
let nav = segue.destinationViewController as! UINavigationController
let secondVC = nav.topViewController as! SettingsViewController
secondVC.delegate = self
}
}
func checkSwitchState() {
if onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
import UIKit
protocol SettingsViewControllerDelegate {
func myVCDidFinish(controller: SettingsViewController, switchState: Bool)
}
class SettingsViewController: UITableViewController {
var delegate: SettingsViewControllerDelegate? = nil
@IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
switchOutlet.on = nsDefaults.boolForKey("savedSwitchSettingDefault")
}
@IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
if (delegate != nil) {
delegate!.myVCDidFinish(self, switchState: switchOutlet.on)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
@IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
nsDefaults.setBool(switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
I believe my problem lies somewhere in loading up the default key for "savedSwitchSettingDefault". Is this correct? Or does the issue lie elsewhere in the code?
Upvotes: 1
Views: 702
Reputation: 114865
You can tidy things up quite a bit by relying on the fact that the default you want is false
and that boolForKey
gives you false
when the key isn't present.
Also, by accessing the setting in viewWillAppear
you can avoid the need for the delegate callback.
ViewController.swift
import UIKit
class ViewController: UIViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
var onFromMain = false
@IBOutlet weak var switchStateLabel: UILabel!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.onFromMain = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
self.checkSwitchState()
}
func checkSwitchState() {
if self.onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
class SettingsViewController: UITableViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
@IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.switchOutlet.on = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
}
@IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
@IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
self.nsDefaults.setBool(self.switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
Upvotes: 1
Reputation: 2778
When retrieving the bool value from User Defaults, boolForKey
will return false if the value doesn't exist. So in this case there's no need for unwrapping. From the documentation:
If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, false is returned.
If the value is getting set (you are sure of it), and the behavior of the app is not working correctly your problem might lie elsewhere.
I would recommend using another approach, declare your "onFromMain" as an optional boolean, then unwrap it when you need it.
var onFromMain: Bool?
...
func checkSwitchState() {
//- This will unwrap your optional or set false if its nil
let switchSate = onFromMain ?? false
//- Then you can set the values based on the value (or the default false)
switchStateLabel.text = switchState ? "On" : "Off"
switchStateLabel.textColor = switchState ? UIColor.greenColor() : UIColor.redColor()
}
Then attach the debugger with a breakpoint and see if the value is being unwrapped or if its defaulting to false.
Also, you are setting your delegate only when the segue is called, depends of the scenario, and if i understand you correctly, you migt not get the value until you have actually navigated to the settings view. So when opening the app (without navigating to the settings view) the onFromMain will never get populated.
Alternatively you can fetch the value on the view did load method to get it straight away when you load the app.
Upvotes: 0