0

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.

Tom
  • 790
  • 2
  • 9
  • 32
  • Possible duplicate of [What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean?](http://stackoverflow.com/questions/32170456/what-does-fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-valu) – Nazmul Hasan Sep 19 '16 at 19:38

5 Answers5

1

You should use:

let buttonState = UserDefaults.standard.bool(forKey: "signatureSwitchState")

If the value is not explicitly set, it will return false.

picciano
  • 22,341
  • 9
  • 69
  • 82
1

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.

Federico Milani
  • 126
  • 2
  • 12
1

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.

vadian
  • 274,689
  • 30
  • 353
  • 361
0

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 (?).

brandonscript
  • 68,675
  • 32
  • 163
  • 220
0

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()
}
Community
  • 1
  • 1
Alexander
  • 59,041
  • 12
  • 98
  • 151