Swift3 optional chaining and unwrapping with error handling [duplicate] - swift

This question already has answers here:
Optional binding with try? and as? still produces an optional type
(2 answers)
Closed 5 years ago.
I am attempting to unwrap an optional using the guard let syntax. The goal with guard let is to unwrap optionals, aborting the procedure if .none/nil is ever encountered.
I had thought that this meant that an optional could not be legally returned from an expression on the right-hand-side of a guard let. However, it seems if you combine two optional-producing expressions into the RHS expression, only one of those optional-producing expressions is evaluated for the purposes of the guard let. For example, the parsedResponse variable below will store an optional:
guard let parsedResponse = try? (JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any]) else {
displayError(withString: "Error parsing data as JSON")
return
}
The try? keyword produces an optional, as does the conditional cast operator as?. If you change either of these two operators to try! or as!, you get the expected result.
Is this the expected behavior? I had assumed that attempting to do something like this would produce behavior similar to optional chaining, and the guard let would execute the 'else' code the moment it encountered .none/nil anywhere along the execution path of the evaluation of the RHS. However, this does not appear to be the case.
What does it even mean to have an optional evaluate into a constant/variable introduced by a guard let?

I think if you rearrange the parens it will work.
guard let parsedResponse = (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)) as? [String:Any] else {

Related

Initializer for conditional binding must have Optional type, not 'AVAudioInputNode' // Swift 4.2 [duplicate]

This question already has answers here:
Conditional Binding: if let error – Initializer for conditional binding must have Optional type
(8 answers)
Closed 4 years ago.
This code:
guard let node = audioEngine.inputNode else { return }
Results in the following error:
Initializer for conditional binding must have Optional type, not 'AVAudioInputNode'
What should I change to make it work?
Tried to delete guard but it doesn't help.
Doing the guard let node = audioEngine.inputNode ... is trying to unwrap an optional value. However, audioEngine.inputNode does not return an optional value.
If you just do let node = audioEngine.inputNode (without the guard let return), it will work.

"if let" not unwrapping an optional value in Swift 4.0

I am implementing CoreML in Swift 4, and here is an issue with if let that I am facing right now,
var pixelBuffer : CVPixelBuffer?
var model: Inceptionv3?
if let prediction = try? self.model?.prediction(image: pixelBuffer!)
{
classifier.text = "I think this is a \(String(describing: prediction.classLabel))." //ERROR..!!!
}
Value of optional type 'Inceptionv3Output?' not unwrapped; did you
mean to use '!' or '?'?
I know what the error means and how to resolve it. What I don't understand is why does it appear?
In the above code I am using if let to get an unwrapped value of prediction. And still it is asking me to unwrap it explicitly.
Short answer:
You are creating a doubly-wrapped optional because of the combination of optional chaining and the use of try?. So, if let is only unwrapping the first layer of doubly-wrapped optional leaving you with an optional variable.
Why is this?
try? is used with a call that throws. If the call does throw an error, the expression returns nil. If the call succeeds, the value is wrapped in an optional.
You are using an optional chain to call prediction on model. If model is nil, then the result of the optional chain is nil. Note, this result didn't throw so the result gets wrapped in an optional by try? resulting in Optional(nil) which is not the same as nil. If the call to prediction succeeds, the result gets wrapped in an optional because of the optional chain, and wrapped in an optional again because of the try?.
So there are three possible results:
Model is not nil and call to prediction returns a valid result of type Inceptionv3Output without throwing. This result becomes Inceptionv3Output? because of the optional chain and Inceptionv3Output?? because of the try?.
Model is not nil, but the call to prediction throws an error. The result of the try? is nil because of the throw.
Model is nil. The result of the optional chain is nil, and that result gets wrapped in an optional again because of try ? resulting in Optional(nil).
So, your if let is unwrapping a value of type Inceptionv3Output?? which means prediction has the type Inceptionv3Output?. The if let succeeds if the call doesn't throw. If the model is nil, then prediction will be nil, otherwise it contains the wrapped value which is the result of the prediction call on the model.
Try unwrapping it this way:
if let model = model,
let prediction = try? model.prediction(image:pixelBuffer!) {
// Your code here
}
You can't use try? with an expression that doesn't throw. So you're forced to unwrap model just to make sure that the expression throws.
In case model was nil, the expression won't execute prediction(image:), and this is why you're to unwrap model before using try?

Swift 2.x to Swift 3, Xcode complaining error : Non-optional expression of type 'String' used in a check for optionals

Migrating from Swift 2.x to Swift 3, I have the warning on my code :
let response=NSString(data:data!,encoding:
String.Encoding.utf8.rawValue)
let responseArray:Array=response!.components(separatedBy: "|VCSPACER|")
if let result:String=responseArray[0] {
if let action:String=responseArray[1] {
if let datas:String=responseArray[2] {
.......
}
}
}
The compiler is warning at line :
"if let action:String=responseArray[0]" and line "if let action:String=responseArray[1]" and "line if let datas:String=responseArray[2]"
with the message
"Non-optional expression of type 'String' used in a check for optionals"
It was perfectly working on Swift 2.X but not on Swift 3. How can I change this code to make it work?
Don't use force unwrapping of optionals. The force unwrapping makes it a non-optional value, hence optional binding cannot be used. Moreover, array subscripting does not return an optional value, so rather than trying to use optional binding to check if the array has that many elements, after unwrapping the array, check if it has as many elements as you want to access.
if let data = data,
let responseArray = String(data: data, encoding: .utf8)?
.components(separatedBy: "|VCSPACER|"),
responseArray.count > 2 {
let result = responseArray[0]
let action = responseArray[1]
let datas = responseArray[2]
}

Can I use `try?` with a generic method?

I have a generic throwing method:
func doSomething<T>() throws -> T? {
// do something
}
This is how I want to call it:
let obj: SomeClass? = try? doSomething() // a compiler error!
But I may not. The compiler gives me an error
Value of optional type 'SomeClass??' not unwrapped; did you mean to use 'try!' or chain with '?'?.
I can use try! or try instead. But I don't like the former because it might crash the app at some point, and I don't like the latter because it's too long for most of the cases (one line becomes five).
Does anybody know how can I keep using try??
Your method is returning an optional. The try? expression adds another level of optional, therefore your expected return value should be a double optional:
let obj: SomeClass?? = try? doSomething()
It's probably not a good idea to combine returning of optional and throws.
You could also remove the second level of optionals using ??:
let obj: SomeClass? = (try? doSomething()) ?? nil
but I really recommend to redesign the API instead of solving double optionals.

Chaining Optionals in Swift

Up until now, I've been unwrapping Optionals in Swift 2.1 like so:
#IBOutlet var commentTextView: UITextView!
if let comment = user["comment"] as? String {
commentTextView.text = comment
}
I never really thought about it, but I think the reason I was doing this was because I was worried that this statement would throw an error if user["comment"] returned something other than a String:
commentTextView.text = user["comment"] as? String
If user["comment"] isn't a String, will the variable on the left of the assignment operator be assigned and throw an error or will the assignment be skipped?
I guess user is in fact a dictionary [String: Any] and what you really do with if let comment = user["comment"] as? String { ... } is not just unwrapping the optional but a conditional type casting (and then unwrapping an optional result of it):
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
Now, to answer your question, if user["comment"] isn't a String then the result will be that commentTextView.text will be assigned nil value, which is bad because its type is String! (implicitly unwrapped optional) about which we hold a promise that it will never be nil. So, yes, there will be an error, an exception actually, but not at the place you would like it to be but at the moment your application will try to access its value assuming that it's not going to be nil.
What you should really do depends on a particular case.
E.g. if you can make user to be a dictionary like [String: String], then you would be able to truly get to unwrapping the optionals and use something like if let comment = user["comment"] { ... }. Or, if you are totally sure that the value for "comment" key will always be there, then you could just do let comment = user["comment"]!.
But if that's not possible then you have to stick with down-casting and the only other thing you can do is to use forced form of it, that is commentTextView.text = user["comment"] as! String. This one at least will produce an exception right at the spot in case if the value at "comment" happens to be not a String but something else.
nil will be assigned to the variable.
If the type of the variable is a non-optional, you'll get a runtime error.
However if user["comment"] is a String you'll get a compiler error about missing ! or ?.
First we need to know of what type the dictionary "user" is.
I assume it is of an unknown type like [String: AnyObject], otherwise why would you try to unwrap it as an String. Let us write a short test to see what happens:
let dict: [String: AnyObject] = ["SomeKey" : 1]
if let y = dict["SomeKey"] as? String {
print(y)
}
You can see clearly that the value of "SomeKey" is an Integer. Trying to unwrap it as an String triggers no error, the "if" statement is just skipped. If an assignment actually happened is hard to prove (maybe by looking at the assembler code) because the variable "y" simply does not exist after the if statement. I assume it will not be created at all.
If the type of the dictionary is known as [String: String] you can omit the try to unwrap it as a String because it's always clear that the type is String.
let dict2: [String: String] = ["SomeKey" : "SomeValue"]
if let y = dict2["WrongKey"] {
// In this case print(y) will not be called because the subscript operator of the dictionary returns nil
print(y)
}
// In this case print(y) will be called because the key is correct
if let y = dict2["SomeKey"] {
print(y)
}