I have a piece of code that runs if a switch has been set in settings as follows:
UserDefaults.standard.bool(forKey: "signatureSwitchState")
let buttonState = UserDefaults.standard.object(forKey: "signatureSwitchState") as! Bool
if buttonState == true {
sign()
}
My problem is if the switch has never been activated the program fails as the compiler states that, "fatal error: unexpectedly found nil while unwrapping an Optional value"
My question is then how best to guard against a nil value when using a bool such as the switch in the above statement.
I've tried if let statements and guard statements but the compiler complains that these cannot be used with a bool.
You should use:
let buttonState = UserDefaults.standard.bool(forKey: "signatureSwitchState")
If the value is not explicitly set, it will return false.
If you're trying to get a bool from the UserDefaults you can use
let buttonState = UserDefaults.standard.bool(forKey: "signatureSwitchState"),
this function returns a Bool so you know that the value can only be true or false and if it doesn't find a value for the key then it will return false. The function you are using
UserDefaults.standard.object(forKey: "signatureSwitchState")
returns a AnyObject? so it can be nil.
Little known, but highly recommended by Apple:
Register defaults values which are considered until the user changes the value the first time.
In applicationDidFinishLaunching – at least before accessing the value the first time – register the key / value pair(s).
let userDefaults = UserDefaults.standard
let defaultValues : [String : Any] = ["signatureSwitchState" : false]
userDefaults.register(defaults: defaultValues)
Now the value is never nil (I know the Bool example is pretty weak)
let buttonState = UserDefaults.standard.bool(forKey: "signatureSwitchState")
The objects take much more advantage of that way than the "primitive" types which are never nil anyway.
I have a tip that might come in useful in the future, though it's not as good aspicciano's answer which is definitely the correct way to go.
UserDefaults.standard.object(forKey: "signatureSwitchState") returns an AnyObject.
If you try to force cast to Bool (as! Bool), this will crash if the returned value isn't a Bool, or if it is nil. Instead, if you conditionally cast (as? Bool), you'll receive a Bool?. This will cast the value to a Bool if it exists, otherwise it'll give you nil (without crashing.
From there, you can convert this Bool? to Bool by using the nil coalescing operator (??). It will return you the Bool value if there is one, otherwise it'll return the default value you give it. In this case, false.
Also, because buttonState is already a Bool, there's no reason to compare it to true in your if statement. Just use it directly:
let buttonState = UserDefaults.standard.object(forKey: "signatureSwitchState") as? Bool ?? false
if buttonState {
sign()
}
There are a couple ways you can do this. Since this is a bool, it's probably easiest just to use nil-coalescing ??:
let buttonState = UserDefaults.standard.object(forKey: "signatureSwitchState") as? Bool ?? false
Or, you could use guard to do it, which is probably more robust:
guard let buttonState = UserDefaults.standard.object(forKey: "signatureSwitchState") as? Bool else { return }
if buttonState == true {
sign()
}
Note that in both cases, you have to change the forced unwrap (!) to an optional unwrap (?).
Related
I don't understand the concept of making a useless constant if the task is only to unwrap the value:
guard let foo = foo else { return }
vs
guard foo != nil else { return }
What is the difference between these statements? And what is the reason to not use the latter?
Here is some example of using guard let:
var array: [String] = ["pineapple", "potato", "corn"]
guard let lastElement = array.last, lastElement == "corn" else { return false }
And not using let:
guard array.last == "corn" else { return false }
Can't I just go with the second approach as more clean, simple and probably more memory efficient?
With your first example, foo becomes a non-Optional. So, you can do this:
guard let foo = foo else { return }
foo.doMethod()
Whereas without the optional binding, you'd still have an Optional:
guard foo != nil else { return }
foo?.doMethod()
There are plenty of times when having a non-Optional value is easier to deal with, including being able to avoid force unwrapping with !, so there's lots of practicality to the first code sample.
In terms of the second version, you could use it where you want to do a nil check but may not actually use the value in the scope in which you're checking it.
In your example with the array of type [String], yes, you can do your second comparison without the optional binding to check to see if there's a last element:
guard array.last == "corn" else { return false }
You are correct that it is cleaner this way. It is extremely unlikely that it would be more "memory efficient" as you speculated, though, as the compiler would likely optimize away the temporary optional binding.
I have declared a type called "JournalEntry" as an optional and have it set to nil. Then when the VC loads I test to make sure that the object has been injected before trying to use it, but I get an error that says "Comparing non-optional value of type 'JournalEntry' to nil always returns true".
But I have it set as an optional and to nil...
Here's the code:
class AddJournalEntryVC: UIViewController, UITextViewDelegate {
var willEdit:Bool?
var entryForEdit:JournalEntry? = nil
override func viewDidLoad() {
super.viewDidLoad()
if isEditing {
guard let entry = entryForEdit, entry != nil else{ //Comparing non-optional value of type 'JournalEntry' to nil always returns true
return
}
dateLabel.text = entry.dateString!
timeLabel.text = entry.timeString!
timestamp = entry.timestamp!
}
}
Where has my thinking gone wrong? Thank you.
Just remove the entry != nil clause and it will work as you require. The if let statement that proceeds it performs the not-nil check already.
guard let entry = entryForEdit else {
return
}
I'm having trouble with this line:
let isBranchLinkKey = "+clicked_branch_link"
guard let didClickBranchLink = params[isBranchLinkKey] as? Int else { return }
didClickBranchLink is nil even though in the debugger the parameter is there.
Below is a screenshot of my debugger:
.
As mentioned in the edited screenshot, paramBranchLink is of a boolean value(Notice __NSCFBoolean in debugger on left side) but you are trying to cast as Int in guard statement. Since in swift both are two different types, the guard statement will fail. You should change your guard statement to get it as Bool value.
guard let didClickBranchLink = params[isBranchLinkKey] as? Bool else { return }
This should give a boolean value in didClickBranchLink which you can use it in your later part of the code.
I have an app where I'm currently using the SwiftKeychainWrapper. Below is the code I have which checks if retrievedString is nil. However I'm still getting retrievedString: nil in the console.
Shouldn't the code in the if-let statement not run, or am I using/understanding if-let incorrectly?
With the given example, what's the correct way to use if-let to unwrap my optional value?
if let retrievedString: String? = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//Value is nil
}
This is because you are setting the value of a optional String, String? KeychainWrapper.stringForKey("username") to another optional String retrievedString.
By trying to set a String? to another String?, the if let check always succeeds, even when the value is nil, because both the types are the same, and both can accept a nil value.
Instead, you should attempt to set the optional string, String? to a non-optional string, String. When Swift tries to set a non-optional String to nil, it will fail, because a non-optional cannot have a nil value. The code will then continue in the else statement
You should use
//notice the removal of the question mark
// |
// v
if let retrievedString: String = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//value is nil
}
You are setting the type of retrievedString to be optional. The whole point of the check is to remove the optional and just have a String.
if let retrievedString: String = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//Value is nil
}
I add #IBAction for button:
#IBAction func addThemeAction(sender: AnyObject) {
var userDefaults:NSUserDefaults=NSUserDefaults.standardUserDefaults()
var itemList:NSMutableArray!=userDefaults.objectForKey("itemList") as NSMutableArray}
When I press button I get fatal error: unexpectedly found nil while unwrapping an Optional value.
There's no object for the key itemList in your NSUserDefaults. Instead of force unwrapping it with !, check if optional is nil and conditionally unwrap it:
let userDefaults = NSUserDefaults.standardUserDefaults()
var itemList = userDefaults.objectForKey("itemList") as? [AnyObject]
if let itemList = itemList {
// itemList is not nil, use it here
} else {
// itemList has never been set, perhaps use some default
}
Also, userDefaults doesn't need to be a var.
You can change [AnyObject] to something else if you know the type (like [String], for example).
One: You can never guarantee that some key is present in your user defaults, so this is a crash waiting to happen. Two: I don't think dictionaries that you read from user defaults are mutable.