1

When I got to a page with this code:

override func viewDidLoad() {
super.viewDidLoad()

//Load Upgrades

    let FourthDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
    var upgrades = FourthDefaults.valueForKey("Upgrades")?.integerValue
    FourthDefaults.synchronize()
    Upgrades = upgrades!
}

I am getting an EXC_BAD_INSTRUCTIONS error on:

Upgrades = upgrades!

In case this helps, this is my saving method.

let FourthDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
        FourthDefaults.setObject(Upgrades, forKey: "Upgrades")
        FourthDefaults.synchronize()

Extra Info: This only happens when I have this code load before I save it. Otherwise it works fine.

If you know how to fix this please respond. Thanks!

DB3
  • 21
  • 5

1 Answers1

1

valueForKey returns an optional. If there is no value for the key “upgrades”, the result will be nil. This will be the case before you save the value for the first time.

This is why you have to write valueForKey("Upgrades")?.integerValue – because if the value returned is nil instead of an AnyObject, you can’t call .integerValue on it. You have to use the ?. operator. Read that operator as “if the result of this is nil, then stop and return nil, otherwise continue and return an optional containing the result of the next method.”

So upgrades is either nil (if there was no such key), or an optional containing the value you want.

If you force-unwrap an optional using !, and it is nil, you will get a runtime assertion, like you’re getting. For this reason you should hardly ever use !. The times you do need to use it are pretty rare and you should think carefully about whether there’s an alternative.

In this case there is a much better alternative – use the ?? operator:

var upgrades = FourthDefaults.valueForKey("Upgrades")?.integerValue ?? 0

(or whatever default value you want in the case of a missing key, if 0 isn’t it)

The ?? reads as: if the expression on the left is nil, then substitute the value on the right instead. Otherwise, use the unwrapped value on the left.

This has the benefit of you not needing the ! anymore, and should eliminate your runtime error.

Alternatively, if you want to execute different code when the value hasn’t already been set, you could write this:

if let upgrades = FourthDefaults.valueForKey("Upgrades")?.integerValue {
    // use upgrades, which will be unwrapped in this block
}
else {
    // handle the value not being set yet
}

For more on why optionals are useful, try reading this answer.

Community
  • 1
  • 1
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • Same as before, if you ever use `!` on a nil value, kaboom. If there is no key "money" then your `as? String` will be nil so `money!` will go bang. Use one of the techniques in my answer instead. Stop using `!` just to make it compile. – Airspeed Velocity Dec 28 '14 at 03:58