user6485277
user6485277

Reputation:

NSUserDefaults optionals in Swift

I'm trying to deal with compiler in case of optional values. Task is very simple, my func fetches user defaults to appear them in tableview. If user launches app for the first time, it setts default values. Setting default values works fine (checked with print log), but fetching causes:

fatal error: unexpectedly found nil while unwrapping an Optional value

I'm already worked with optionals for a long time, but, perhaps I'm still confused about them, 'cos I see that everything seems to be correct and even compiler says, that everything is ok.

func getFiltersSetts() -> [String] {
    let userDefs = NSUserDefaults.standardUserDefaults()
    var defsArray = [String]()
    if (userDefs.objectForKey("gender") != nil) {
        defsArray.append((userDefs.objectForKey("gender")?.stringValue)!)
        defsArray.append((userDefs.objectForKey("age")?.stringValue)!)
        defsArray.append((userDefs.objectForKey("online")?.stringValue)!)
    }
    else {
        userDefs.setObject("Male", forKey: "gender")
        userDefs.setObject("21-30", forKey: "age")
        userDefs.setObject("Online", forKey: "online")
    }
    return defsArray
}

Upvotes: 2

Views: 2646

Answers (4)

vadian
vadian

Reputation: 285059

Apple highly recommends to set default values via registerDefaults of NSUserDefaults.

As soon as possible (at least before the first use) set the default values for example in applicationDidFinishLaunching:

let userDefs = NSUserDefaults.standardUserDefaults()
let defaultValues = ["gender" : "Male",  "age" : "21-30",  "online" : "Online"]
userDefs.registerDefaults(defaultValues)

The default values are considered until a new value is set.

The benefit is you have never to deal with optionals nor with type casting.

func getFiltersSetts() -> [String] {
    let userDefs = NSUserDefaults.standardUserDefaults()
    return [userDefs.stringForKey("gender")!, 
            userDefs.stringForKey("age")!, 
            userDefs.stringForKey("online")!] 

}

The forced unwrapping is 100% safe because none of the keys can ever be nil.

Please read Registering Your App’s Default Preferences in Preferences and Settings Programming Guide

Upvotes: 2

Amit Singh
Amit Singh

Reputation: 2698

You are storing all the value as String and then at the time of fetching you are typecast the value again in String

userDefs.objectForKey("gender")?.stringValue

A String can not be convert as String again.

try to access as

userDefs.stringForKey("gender")

Upvotes: 0

Daniel Ormeño
Daniel Ormeño

Reputation: 2778

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!

Upvotes: 3

Emptyless
Emptyless

Reputation: 3050

You can create a default key "firstBootCompleted". Then use:

let defaults = NSUserDefaults.standardUserDefaults()
if (defaults.boolForKey("firstBootCompleted") == false) {
    defaults.setObject(true, forKey: "firstBootCompleted")

    // Initialize everything for your first boot below

} else {

    // Initialize everything for not your first boot below

}

The reason this works is that boolForKey returns false when the object for key is nil

Upvotes: 0

Related Questions