How to safely unwrap a value from user defaults? [duplicate] - swift

This question already has answers here:
Swift optionals - warning on conditional cast from 'x' to 'x' always succeeds
(2 answers)
If let clause when getting from NSUserDefaults()
(2 answers)
Closed 5 years ago.
if let seen: Bool = defaults.bool(forKey: UtilitiesKeys.mainTutorialSeen) {
return seen
}
return false
if i do this swift shows me an issue:
Conditional cast from 'Bool' to 'Bool' always succeeds, Non-optional
expression of type 'Bool' used in a check for optionals.
Since there might not be a value for my key, I don't want to force unwrap it. Now obviously it's working like this, but how do I safely unwrap the value without having swift complaining?
I have tried to use guard as well...

if let seen = UserDefaults.standard.object(forKey: UtilitiesKeys.mainTutorialSeen)) as? Bool {
return seen
}else {
return false
}

Apple document says:
/*!
-boolForKey: is equivalent to -objectForKey:, except that it converts the returned value to a BOOL. If the value is an NSNumber, NO
will be returned if the value is 0, YES otherwise. If the value is an
NSString, values of "YES" or "1" will return YES, and values of "NO",
"0", or any other string will return NO. If the value is absent or
can't be converted to a BOOL, NO will be returned.
open func bool(forKey defaultName: String) -> Bool
Initializer for conditional binding must have Optional type, not 'Bool'
hope this help you.

As suggested in doc:
-boolForKey: is equivalent to -objectForKey:, except that it converts the returned value to a BOOL. If the value is an NSNumber, NO will be returned if the value is 0, YES otherwise. If the value is an NSString, values of "YES" or "1" will return YES, and values of "NO", "0", or any other string will return NO. If the value is absent or can't be converted to a BOOL, NO will be returned.
open func bool(forKey defaultName: String) -> Bool
It is not an optional anymore. So you don't need to cast it with if let.
You can directly use:
let seen = defaults.bool(forKey: UtilitiesKeys.mainTutorialSeen)

Related

"Value of optional type 'Set<String>?' must be unwrapped", but I didn't ask for an optional

Xcode complains "Value of optional type 'Set?' must be unwrapped to refer to member 'contains' of wrapped base type 'Set'"
Here's the function:
func talks_to (_ dialog_dict: Dictionary<String, Set<String>>, one: String, two: String) -> Bool {
return dialog_dict[one].contains(two)
}
This is a nested function, meant only to make logic clearer, and the parameters are guaranteed to be non-nil by the outer code. How do I get swift to understand this?
Any time you fetch an item from a dictionary using subscripting, the result is an Optional because the key you use might not be found. Sh_Khan gave you a nice elegant solution: (voted)
return dialog_dict[one]?.contains(two) == true
That works because nil does not equal true, but the compiler will unwrap it and check the value inside to see if it equals true if it's not nil. So if the result of dialog_dict[one] is nil or false, it does not equal true. Only if dialog_dict contains a value for the key one and that value is true does the expression return true.
Make it
return dialog_dict[one]?.contains(two) == true
or
return dialog_dict[one]!.contains(two)
this dialog_dict[one] returns optional

How to check for nil inside extension

Following code
extension String {
func isValidEmail() -> Bool {
if self == nil { return false }
Gives me error Value of type 'String' can never be nil, comparison isn't allowed
Is it possible to somehow check for nil here?
Checking for nil there is not required.
That function is an instance function on String.
It can only ever be run on an instance of String.
Secondly Swift does not have "nil messaging" like Objective-C so the String instance that the function is called on HAS to be not nil. Even in Objective-C this would still not matter as the function would not run if called on a nil String.
So, the message is correct, Value of type "String" can never be nil.
You could check if your string is empty as:
var myString : String?
if myString.isEmpty {
// do whatever you want ..
}
That's has more sense..
Recall that in Swift, values of type String can never be nil; if you wanted a nillable value, you'd have to declare it String?.
So no, there is no way to check if a String variable is set to nil, because it can't.

Why I get optional value if I didn't mark it as optional [duplicate]

class X {
static let global: [String:String] = [
"x":"x data",
"y":"y data",
"z":"z data"
]
func test(){
let type = "x"
var data:String = X.global[type]!
}
}
I'm getting the error: Value of optional type 'String?' not unwrapped.
Why do I need to use ! after X.global[type]? I'm not using any optional in my dictionary?
Edited:
Even if X.global[type] may not exist for the type, force unwrapping will still crash on runtime. A better approach may be:
if let valExist = X.global[type] {
}
but Xcode is giving me the wrong idea by hinting about optional type.
Dictionary accessor returns optional of its value type because it does not "know" run-time whether certain key is there in the dictionary or not. If it's present, then the associated value is returned, but if it's not then you get nil.
From the documentation:
You can also use subscript syntax to retrieve a value from the dictionary for a particular key. Because it is possible to request a key for which no value exists, a dictionary’s subscript returns an optional value of the dictionary’s value type. If the dictionary contains a value for the requested key, the subscript returns an optional value containing the existing value for that key. Otherwise, the subscript returns nil...
In order to handle the situation properly you need to unwrap the returned optional.
There are several ways:
Option 1:
func test(){
let type = "x"
if var data = X.global[type] {
// Do something with data
}
}
Option 2:
func test(){
let type = "x"
guard var data = X.global[type] else {
// Handle missing value for "type", then either "return" or "break"
}
// Do something with data
}
Option 3:
func test(){
let type = "x"
var data = X.global[type] ?? "Default value for missing keys"
}
If we look at the Dictionary implementation, subscript is returning a ValueType as optional because it doesn't know if the key is exists or not:
//Reading a key that is not present in `self` yields `nil`.
//Writing `nil` as the value for a given key erases that key from `self`.
subscript (key: KeyType) -> ValueType?
So when we try to get a value from our Dictionary we get it as an optional from the subscript; that is we have to unwrap the optional to get the underlying object. As mentioned in earlier answers, option2 is preferred.
guard var data = X.global[type] else {
//key = 'type' doesn't exists
}
//key exists so do something with 'data'

Optional issue converting String to Int in Swift 1.2

I can't figure out why this 'optional' isn't working in scenario 1, but without the optional ? it works in scenario 2.
Using Swift v 1.2, xCode 6.2
var stuff = "6t"
// SCENARIO 1
// Why is this failing when stuff contains non-digit characters?
// i.e. it works if stuff = "45".
if let value: Int? = stuff.toInt() {
println("value \(value!)")
}
// SCENARIO 2
// This works!
if let value = stuff.toInt() {
println("val3 \(value)")
}
For Reference also see these SO Answers:
* I wonder if the Sift 1.2 example/Answer here is just plain wrong?
Swift - Converting String to Int
Converting String to Int in Swift
The first IF is always true.
Infact in both cases:
when the toInt() returns a valid Int
when returns nil
the if let will succeed (and yes, the first IF is useless).
Specifically in your code toInt() returns nil in both scenarios.
But in your first scenario you are simply accepting nil as a valid value to enter the THEN block.
There is no point of using if let value: Int?. If the if let works, then the value is an Int. There is no way that it could be nil. Therefore, you do not need to declare it as an optional.

Why do I still need to unwrap Swift dictionary value?

class X {
static let global: [String:String] = [
"x":"x data",
"y":"y data",
"z":"z data"
]
func test(){
let type = "x"
var data:String = X.global[type]!
}
}
I'm getting the error: Value of optional type 'String?' not unwrapped.
Why do I need to use ! after X.global[type]? I'm not using any optional in my dictionary?
Edited:
Even if X.global[type] may not exist for the type, force unwrapping will still crash on runtime. A better approach may be:
if let valExist = X.global[type] {
}
but Xcode is giving me the wrong idea by hinting about optional type.
Dictionary accessor returns optional of its value type because it does not "know" run-time whether certain key is there in the dictionary or not. If it's present, then the associated value is returned, but if it's not then you get nil.
From the documentation:
You can also use subscript syntax to retrieve a value from the dictionary for a particular key. Because it is possible to request a key for which no value exists, a dictionary’s subscript returns an optional value of the dictionary’s value type. If the dictionary contains a value for the requested key, the subscript returns an optional value containing the existing value for that key. Otherwise, the subscript returns nil...
In order to handle the situation properly you need to unwrap the returned optional.
There are several ways:
Option 1:
func test(){
let type = "x"
if var data = X.global[type] {
// Do something with data
}
}
Option 2:
func test(){
let type = "x"
guard var data = X.global[type] else {
// Handle missing value for "type", then either "return" or "break"
}
// Do something with data
}
Option 3:
func test(){
let type = "x"
var data = X.global[type] ?? "Default value for missing keys"
}
If we look at the Dictionary implementation, subscript is returning a ValueType as optional because it doesn't know if the key is exists or not:
//Reading a key that is not present in `self` yields `nil`.
//Writing `nil` as the value for a given key erases that key from `self`.
subscript (key: KeyType) -> ValueType?
So when we try to get a value from our Dictionary we get it as an optional from the subscript; that is we have to unwrap the optional to get the underlying object. As mentioned in earlier answers, option2 is preferred.
guard var data = X.global[type] else {
//key = 'type' doesn't exists
}
//key exists so do something with 'data'