Swift and Nsuserdefaults - Exc_Bad_Instruction When User Defaults Empty

Swift and NSUserDefaults - EXC_BAD_INSTRUCTION when user defaults empty

edit
This answer is no longer updated since in the final Swift release Apple changed the method declaration of objectForKey on NSUserDefaults (and many other methods bridged from objective c) to return an optional ( ? ) and no more an Implicitly Unwrapped Optional ( ! )

end edit

Ok, I read better the docs and the meaning of the ! in a declaration, which is different from a ! when using a variable.

! in a declaration means that that variable is implicitly unwrapped (on the Swift book, read "Implicitly Unwrapped Optionals" and "Unowned References and Implicitly Unwrapped Optional Properties" chapters to understand better what it means

In simple words, it means that the return value can still be nil, but since it happens "rarely", the return value / variable is not marked as optional (with the ?).
This is made to avoid the needing for the programmer to unwrap the value every time (checking with an if statement and unwrapping the value) having a cleaner code.

However, the first problem was that the EXC_BAD_INSTRUCTION was given on the wrong line, since with this statement

let defaultKey = AppUserDefaults.SelectedMonth.value()
let defaults = NSUserDefaults.standardUserDefaults()
let result : AnyObject = defaults.objectForKey(defaultKey)
return NSString(UTF8String: "")

no error is given.(the result was not returned but the objectForKey call did not fail)
So this brought me on the right way again and understanding the meaning of Implicitly Unwrapped Optionals

The solutions are simple, since the returned value can be nil the variable selectedMonth should have a ? (to be marked as optional) or a ! (to be marked as implicitly unwrapped optional)

Examples

1) return AnyObject? optinal value as a type for the var selectedMonth

var selectedMonth : AnyObject? {
get {
return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value())

}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
NSUserDefaults.standardUserDefaults().synchronize()
}
}

2) return an optional NSString?, cast the return type to AnyObject? and then to NSString?

var selectedMonth : NSString? {
get {
return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value()) as AnyObject? as NSString?
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
NSUserDefaults.standardUserDefaults().synchronize()
}
}

this is like writing

var selectedMonth : NSString? {
get {
var returnValue : AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value())
return returnValue as NSString?
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
NSUserDefaults.standardUserDefaults().synchronize()
}
}

3) return an implicitly wrapped NSString!

var selectedMonth : NSString! {
get {
var returnValue : AnyObject! = NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value())
return returnValue as NSString!
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
NSUserDefaults.standardUserDefaults().synchronize()
}
}

4) short version

var selectedMonth : NSString! {
get {
return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value()) as AnyObject! as NSString!
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
NSUserDefaults.standardUserDefaults().synchronize()
}
}

NSUserDefaults optionals in Swift

You are force unwrapping your optionals, and you should get them as strings before appending them to your array.

A cleaner way to set the defaults would be to coalesce the unwrapping of your optionals, Try the following approach:

func getFiltersSetts() -> [String] {
let userDefs = NSUserDefaults.standardUserDefaults()
var defsArray = [String]()
defsArray.append(userDefs.stringForKey("gender") ?? "Male")
defsArray.append(userDefs.stringForKey("age") ?? "21-30")
defsArray.append(userDefs.stringForKey("online") ?? "Online")
return defsArray
}

The code above uses the coalesce (??) operator. If your optional, say userDefs.stringfForKey("gender"), returns nil, the coalesce operator will use the default value "Male".

Then at a later time you can save your user defaults (or create them) if they haven't been set before.

Also, is worth noticing that you should be unwrapping your optionals using the if let notation. Instead of comparing if its != nil, as this will prevent you from force unwrapping them inside the code block.

I hope this helps!

Store [String] in NSUserDefaults

The following code should help you resolve your problem:

import UIKit

class ViewController: UIViewController {

var food: [String] {
get {
if let returnValue = NSUserDefaults.standardUserDefaults().objectForKey("food") as? [String] {
return returnValue
} else {
return ["muesli", "banana"] //Default value
}
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "food")
NSUserDefaults.standardUserDefaults().synchronize()
}
}

override func viewDidLoad() {
super.viewDidLoad()

print(food) // prints: ["muesli", "banana"] (at first launch)

food = ["cake"]
print(food) // prints: ["cake"]

food += ["spaghetti"]
print(food) // prints: ["cake", "spaghetti"]

food = []
print(food) // prints: []

NSUserDefaults.standardUserDefaults().setObject(nil, forKey: "food")
print(food) // prints: ["muesli", "banana"]
}

}

However, with the previous code, if you set food = [], you will have a problem as food won't return ["muesli", "banana"]. In order to avoid this, you may prefer the following code:

import UIKit

class ViewController: UIViewController {

var food: [String] {
get {
if let returnValue = NSUserDefaults.standardUserDefaults().objectForKey("food") as? [String] {
return returnValue == [] ? ["muesli", "banana"] : returnValue
} else {
return ["muesli", "banana"] //Default value
}
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "food")
NSUserDefaults.standardUserDefaults().synchronize()
}
}

override func viewDidLoad() {
super.viewDidLoad()

print(food) // prints: ["muesli", "banana"] (at first launch)

food = ["cake"]
print(food) // prints: ["cake"]

food += ["spaghetti"]
print(food) // prints: ["cake", "spaghetti"]

food = []
print(food) // prints: ["muesli", "banana"]

NSUserDefaults.standardUserDefaults().setObject(nil, forKey: "food")
print(food) // prints: ["muesli", "banana"]
}

}

Receive user defaults which we stored when user logged in (swift3...)

You just need to define sections to be an instance property of your class, then you can access it anywhere from your class.

class MyViewController: UIViewController {
var sections: [Section]?

override func viewDidLoad() {
super.viewDidLoad()
// Receive user defaults which we stored when user logged in

let defaults = UserDefaults.standard
let firstname = defaults.string(forKey: "usernameValue")
let lastname = defaults.string(forKey: "nameValue")
let connectEmail = defaults.string(forKey: "emailValue")
guard let eventMois = defaults.array(forKey: "event_mois") as? [Section] else {return}

if let sections = sections {
sections.append(eventMois)
} else {
sections = eventMois
}
}

}

And use the Swift naming convention, which is lower-camelcase for variable names.

Why isn't UserDefaults.standard.set saving my array? After the application is removed from background the data is cleared

The only line in your code that gets the value out of the UserDefaults is...

UserDefaults.standard.object(forKey: "event")

This doesn't actually do anything with that value. It gets it... and then throws it away.

This will make it look like it isn't being saved.

Change it to ...

event = UserDefaults.standard.object(forKey: "event")

Get NSUserDefault array

var userDefaults = NSUserDefaults.standardUserDefaults()
var todo = userDefaults.objectForKey("todolist") as NSArray;

if let td = todo {
// Use td
} else {
// todo == nil
}

accessing NSUserDefaults within cellForRow(at:IndexPath) in Swift, memory usage

According to Apple

At runtime, you use UserDefaults objects to read the defaults that
your app uses from a user’s defaults database. UserDefaults caches the
information to avoid having to open the user’s defaults database each
time you need a default value.

You should be 'ok' to retrieve the info in cellForRow, as its likely sitting in a dictionary in memory (assumption), fetched by the key you provide, however to vadian's point, you could just put it in a model or property and eliminate the assumption.

Also, consider if that data will be changed by another process and if you need to observe UserDefaults key.



Related Topics



Leave a reply



Submit