Why is this still optional? - swift

I make a query that returns a NSNumber. I then attempt to cast the NSNumber to String. For some reason, it always prints/ compares as an optional...but when I check the variables type it says string...Why is it optional? I need it to be a string!
let whoseTurn = selectedGame?["currentSender"]
let whoseTurnAsString: String = String(describing: whoseTurn)

if let whoseTurn = selectedGame?["currentSender"] as? NSNumber {
let whoseTurnAsString = "\(whoseTurn)"
print(whoseTurnAsString)
}
This is the right way to do optional chaining and make sure you are not forcing an optional

whoseTurn is an optional wrapping your NSNumber. You aren't "casting" it here to a string, you are making a string that "describes" it, and in this case that description includes the fact that whoseTurn is an optional... If you don't want that you'll need to unwrap it,
let whoseTurnAsString: String = String(describing: whoseTurn!)
(note the ! at the end)

This line of code let whoseTurn = selectedGame?["currentSender"] will return an optional.
This line of code let whoseTurnAsString: String = String(describing: whoseTurn) will return a String describing that optional value, which will be a string like this: Optional(5) or Optional(6). It describes the value saying that it is an optional.
So you need to unwrap that optional in order to get the wrapped value, you can force unwrap selectedGame like this:
let whoseTurn = selectedGame!["currentSender"] and then use the normal String initializer like this: String(whoseTurn).
Or, preferably, safely unwrap it like this:
if let whoseTurn = selectedGame?["currentSender"] {
let whoseTurnAsString = String(whoseTurn)
}

String can be optional also '?' or '!' indicates optional, check documentation on optionals.

Related

Swift 4: Could not cast value of type '__NSCFNumber' to 'NSString'

I have tried
self.adc_role_id = String(res["adc_role_id"])
self.adc_role_id = "\(res["adc_role_id']"
self.adc_role_id = (\(res["adc_role_id"] as? String)!
but still get
Could not cast value of type '__NSCFNumber' to 'NSString'
I added the dump of res[4] below
As new as I am to Swift, I don't know anything else to try
In Swift 4, the String initializer requires the describing: argument label.
I don't know if this will solve your problem, but your first line of code should be written:
self.adc_role_id = String(describing: res["adc_role_id"])
In your screenshot we can see that res["adc_role_id"] is an NSNumber.
To transform an NSNumber to a String you should use its stringValue property.
And since a dictionary gives an Optional, you should use optional binding to safely unwrap it.
Example:
if let val = res["adc_role_id"] {
self.adc_role_id = val.stringValue
}
You could also, if you want, use string interpolation instead of the property:
if let val = res["adc_role_id"] {
self.adc_role_id = "\(val)"
}
but I think using the property is more relevant.
If for some reason the compiler complains about the type of the content, cast it:
if let val = res["adc_role_id"] as? NSNumber {
self.adc_role_id = val.stringValue
}
Note that you should not use String(describing:) because this initializer will try to represent the string in many ways, and some of them will give inaccurate and unexpected results (for example, if String(describing:) resolves to use the debugDescription property, as explained in the documentation, you may get a totally different string than the one you want).
It's also worth noting that using String(describing:) with an optional value such as your dictionary will resolve to a wrong string: String(describing: res["adc_role_id"]) will give Optional(yourNumber)! This is why Mike's answer is wrong. Be careful about this. My advice is to avoid using String(describing:) altogether unless for debugging purposes.
The error message is clear and the dump is clear, too.
The value is not a String, it's an Int(64) wrapped in NSNumber
Optional bind the value directly to Int (NSNumber is implicit bridged to Int) and use the String initializer.
if let roleID = res["adc_role_id"] as? Int {
self.adc_role_id = String(roleID)
}
Please conform to the naming convention that variable names are camelCased rather than snake_cased

Type cast optional values

Whenever we need to du something with an optional value in swift we need to unwrap it to operate, not on the optional, but on the value ”inside” the optional.
var optionalString:String? = "Hello"
optionalString!.append(" World!")
Note the exclamation mark on the second line.
But when using the optional type cast operator (as?) on an optional value, one does not need to unwrap the optional, we just provide it with the optional itself, and it just magically works.
let sneakyString:Any? = "Hello!"
let notSoSneakyString = sneakyString as? String
Note the absent exclamation mark on the second line.
The magic is a bit more obvious if we spell it out:
let sneakyString:Any? = Optional("Hello")
let notSoSneakyString = sneakyString as? String
Its not a string we're trying to cast but an enum with a string as an associated value.
I would expect that I would have to do this:
let sneakyString:Any? = "Hello!"
let notSoSneakyString = sneakyString! as? String
Note the exclamation mark on the second line.
Does the type cast operators act on optionals and non optionals in the same way?
The as? operator makes a cast if it possibly can, and returns either the casted object or nil. It handles nil values too, so that if sneakyString were nil, you wouldn't have been able to cast it to a String and the cast would have failed. Same behaviour as if it were non-nil but not castable to String.
In other words, you don't need the ! because as? handles nil values automatically itself.

most concise way of unwrapping and casting optional

I have a bit of code to get a string out of userDefaults:
if let userString = (userDefaults.objectForKey("user")) {
userTextField.stringValue = userString as! String
}
First, I have to see if the optional is not nil. Then I have to cast it as a string from AnyObject.
Is there a better way of doing this? maybe a one liner?
Note that your forced cast as! String will crash if a default value for the key "user" exists, but
is not a string. Generally, you can combine optional binding (if let) with an optional cast (as?):
if let userString = userDefaults.objectForKey("user") as? String {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
But actually NSUserDefaults has a dedicated method for that purpose:
if let userString = userDefaults.stringForKey("user") {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
If you want to assign a default string in the case that
the default does not exist, or is not a string, then use the
nil-coalescing operator ??, as demonstrated in
Swift issue with nil found while unwrapping an Optional value NSDefautlts, e.g.:
userTextField.stringValue = userDefaults.stringForKey("user") ?? "(Unknown)"
For the special case NSUserDefaults the best – and recommended – way is to use always non-optional values.
First register the key / value pair in AppDelegate as soon as possible but at least before using it.
let defaults = NSUserDefaults.standardUserDefaults()
let defaultValues = ["user" : ""]
defaults.registerDefaults(defaultValues)
The benefit is you have a reliable default value of an empty string until a new value is saved the first time. In most String cases an empty string can be treated as no value and can be easily checked with the .isEmpty property
Now write just
userTextField.stringValue = userDefaults.stringForKey("user")!
Without arbitrary manipulation of the defaults property list file the value is guaranteed to be never nil and can be safely unwrapped, and when using stringForKey there is no need for type casting.
Another way that i like much to clean this up is to do each of your checks
first, and exit if any aren’t met. This allows easy understanding of what
conditions will make this function exit.
Swift has a very interesting guard statements which can also be used to avoid force unwrap crashes like :
guard let userString = userDefaults.objectForKey("user") as? String else {
// userString var will accessible outside the guard scope
return
}
userTextField.stringValue = userString
Using guards you are checking for bad cases early, making your
function more readable and easier to maintain. If the condition is not
met, guard‘s else statement is run, which breaks out of the function.
If the condition passes, the optional variable here is automatically
unwrapped for you within the scope that the guard statement was
called.

Convert optional string to int in Swift

I am having troubles while converting optional string to int.
println("str_VAR = \(str_VAR)")
println(str_VAR.toInt())
Result is
str_VAR = Optional(100)
nil
And i want it to be
str_VAR = Optional(100)
100
At the time of writing, the other answers on this page used old Swift syntax. This is an update.
Convert Optional String to Int: String? -> Int
let optionalString: String? = "100"
if let string = optionalString, let myInt = Int(string) {
print("Int : \(myInt)")
}
This converts the string "100" into the integer 100 and prints the output. If optionalString were nil, hello, or 3.5, nothing would be printed.
Also consider using a guard statement.
You can unwrap it this way:
if let yourStr = str_VAR?.toInt() {
println("str_VAR = \(yourStr)") //"str_VAR = 100"
println(yourStr) //"100"
}
Refer THIS for more info.
When to use “if let”?
if let is a special structure in Swift that allows you to check if an Optional holds a value, and in case it does – do something with the unwrapped value. Let’s have a look:
if let yourStr = str_VAR?.toInt() {
println("str_VAR = \(yourStr)")
println(yourStr)
}else {
//show an alert for something else
}
The if let structure unwraps str_VAR?.toInt() (i.e. checks if there’s a value stored and takes that value) and stores its value in the yourStr constant. You can use yourStr inside the first branch of the if. Notice that inside the if you don’t need to use ? or ! anymore. It’s important to realise thatyourStr is actually of type Int that’s not an Optional type so you can use its value directly.
Try this:
if let i = str_VAR?.toInt() {
println("\(i)")
}

"if let" statement executed despite value being nil

I have an "if let" statement that is being executed, despite the "let" part being nil.
if let leftInc : Double? = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!]! {
println(leftInc)
let valueString : String = formatter.stringFromNumber(NSNumber(double: leftInc!))!
self.leftIncisorTextField?.text = valueString
self.btnLeftIncisor.associatedLabel?.text = valueString
}
// self.analysis.inputs is a Dictionary<String, Double?>
The inputs dictionary holds information entered by the user - either a number, or nil if they haven't entered anything in the matching field yet.
Under the previous version of Swift, the code was written as this:
if let leftInc : Double? = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!]?? {
and worked correctly.
I saw a similar question here, but in that instance the problem seemed to be the result of using Any?, which is not the case here.
Swift 2.2
In your if let you define another optional, that's why nil is a legitimate case. if let is intended mainly to extract (maybe) non optional value from an optional.
You might try:
if let leftInc : Double = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!].flatMap ({$0}) {
// leftInc is not an optional in this scope
...
}
Anyway I'd consider to not do it as a one liner but take advantage of guard case. Just in order to enhance readability. And avoid bang operator (!).
The if-let is for unwrapping optionals. You are allowing nil values by setting the type to an optional Double.
The if statement should be:
if let leftInc = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!] as? Double{
...
}
This will attempt to get an object out of inputs, if that fails it returns nil and skips it. If it does return something it will attempt to convert it to a Double. If that fails it skips the if statement as well.
if inputs is a dictionary like [Something:Double] then you don't need the last as? Double as indexing the dictionary will return a Double?
I recommend reading the swift book on optional chaining.
You could break it down further -
if let optionalDouble = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!], leftInc = optionalDouble {
....
}
as your dictionary has optional values - this way of writing it might make it clearer what's going on
if let k = dict["someKey"]{}, dict["someKey"] will be an object of type Any
this can bypass a nill
So do a typecast to get it correct like if let k = dict["someKey"] as! String {}