I have a code snippet here that works but am curious if there is a cleaner way to accomplish the same thing. I haven't seen anything exactly like this so far.
Logic I want to Achieve
The error is nil or is not a SpecialError
The error is non-nil BUT .foo() returns false
Code
enum SpecialError: Error {
func foo() -> Bool
}
let error: Error? // Some optional Error is passed in
if let myError = error as? SpecialError, myError.foo() {
// Don't care about this case
} else {
// This is the case I'm interested in
bar()
}
I'm curious if there is a better way to accomplish this if let else logic.
I may be misunderstanding, but it seems like nothing happens in the first branch of the if statement and you want to cut it down to just the second part? In that case, this should work for you:
if !((error as? SpecialError)?.foo() ?? false) {
bar()
}
This will execute bar() if:
1. error is nil
2. error is not a SpecialError
3. foo() returns false
The condition you want is for when the expression (error as? SpecialError)?.foo() evaluates to either:
nil, in which case error is not a SpecialError, or is nil.
false, in which case error is a SpecialError, but foo() returned false.
In your case, one way to express this is by taking advantage of the fact that the equality operators are overloaded for optionals, and saying:
if (error as? SpecialError)?.foo() != true {
bar()
}
As we're using the overload of != that compares optionals, true will be promoted to a Bool?, so we're checking that (error as? SpecialError)?.foo() is not a .some(true), which in this case is equivalent to checking if it is .some(false) or .none.
If I understand your question correctly, you're probably looking for something like this:
if !((error as? SpecialError)?.foo() ?? false) {
How about translating it exactly as you explained it:
if error == nil || !(error! is SpecialError) || !error!.foo() {
bar()
}
The short-circuiting of or will prevent the force unwraps of error from being a problem.
I think your code is hard to read, and John Montgomery's code is even harder to read and I predict it will be hard to maintain: imagine another developer looking at this code a year from now and asking you what it is doing, or even worse, that developer cannot ask you as you are no longer available. Imagine yourself looking at this code even even a couple months from now.
if let myError = error as? SpecialError, myError.foo() is convoluted, perhaps being a bit too clever. It contains too much logic to be long term readable by a team of developers.
Your first block could just check if myError is nil
Your second block could just check if is myError of type SpecialError
//you could use a guard statement here
if myError == nil {
return;
}
if let myError = error as? SpecialError {
//ok myError is type SpecialError it has a foo method I can call
if(myError.foo()) {
//do something
} else {
//do something else
}
} else { //myError not type SpecialError and is not nil
// This is the case I'm interested in
bar()
}
if let theFoo = (error as? SpecialError)?.foo(){
if theFoo != true{
bar()
} //in case you want to do handling if theFoo is true
} //in case you want to do handling if error is not SpecialError
Related
Complete newb to Swift 4 here. How can the following be more compactly written in Swift?
if myVar != nil {
if !myVarList.contains(myVar!) {
myVarList.append(myVar!)
}
}
I tried searching for examples of guard but couldn't find anything. I think Swift allows me to more compactly deal with the nil checking, but not sure how to go about it when combined with the nested conditional.
Just use if let to conditional unwrap the optional and do the other check in the same line, the comma represents a boolean AND operator
if let item = myVar, !myVarList.contains(item) {
myVarList.append(item)
}
If an (unwrapped) optional is going to be used after the check never write != nil
guard is only useful if an evaluation to false exits the scope
func foo() {
guard let item = myVar, !myVarList.contains(item) else { return }
myVarList.append(item)
}
I know there are some similar questions around, but I couldn't find one specific to my issue.
I have a request where I want to check for the presence of the error key. it is not present everything is fine, if not I should handle the error. Currently, I have it implemented as follows:
if let error = json["error"] {
// handle error
}
else {
// handle success
}
I would like to use a guard statement here to have the success case unindented. The only way I came up with is
guard json["error"] == nil else {
let error = json["error"]!
// handle error
}
// handle success
but that seems wrong to me with the !. Are there any other approaches to this?
In your guard code you would have to have a return statement in the else block. Like this...
guard json["error"] == nil else {
let error = json["error"]!
// handle error
return
}
// handle success
But you are correct. Having to force unwrap the error is not ideal.
So in this case. I think guard is the wrong solution. Instead use if but return from the conditional block. This removes the need for using an else block.
if let error = json["error"] {
print(error)
// handle error
return
}
// handle success...
// no need for else block. Just return from the if in the error case.
The difference between guard let and if let is where the unwrapped optional is scoped.
With guard it is scoped outside the block with if it is scoped inside the block.
An idea for your issue was proposed on the Swift Evolution mailing list:
"guard not let" optional binding
https://forums.swift.org/t/idea-guard-not-let-optional-binding/2614
[it is] fairly common that you want to check that an optional is nil, and still bail if it isn’t (maybe using the value that you now know exists), e.g:
guard cachedValue == nil else { return cachedValue! }
cachedValue = //… expensive calculation
It seems a little bit “unfair” that we have this lovely clean let syntax when checking for Optional.Some, but we to have to do this ugly manual check against nil and explicit unwrap when checking for Optional.None. There is literally no other way to satisfy the guard statement; our optional bindings only go one-way can’t be evaluated.
Unfortunately that construct does not currently exist in Swift.
The alternatives are the slightly awkward/duplicated guard syntax, possibly with a force-unwrap:
guard json["error"] == nil else {
return json["error"]!
}
or using if-let (which does not enforce scope-exit like guard):
if let error = json["error"] {
return error
}
I'm writing some debug code to which I need to pass a parameter of type Any. For printing purposes I'd like to unwrap the parameter value iff it's an optional, but I can't figure out how to test that - every syntactic form I can think of is rejected by the compiler. E.g.,
switch val {
case as Optional<Any>:
.
.
and a variety of let forms (including trying .dynamicType) aren't legitimate. Does anyone know how to actually do this? Overall, what I'm trying to accomplish is such that whether or not the value is an optional, I get the actual value into a string and not Optional.
Martin is absolutely correct. From the linked post, modified slightly because I wanted a different return for nil:
func unwrap(any:Any, ifNil: Any = "nil") -> Any {
let mi = Mirror(reflecting: any)
if mi.displayStyle != .Optional {
return any
}
if mi.children.count == 0 { return ifNil }
let (_, some) = mi.children.first!
return some
}
What is the best way to handle an init that might fail in Swift? For example, you create an instance of class that depends on a certain resource that might not be available.
Apparently we have 2 options:
A bailable init that returns nil (the Cocoa way)
An init that throws an error
See below
enum ThingError: ErrorType{
case crap
}
class Thing {
init(c: Int) throws{
if c < 0 {
throw ThingError.crap
}
}
}
var c = try Thing(c: 3)
do{
var d = try Thing(c: -4)
}catch{
print("oh vey!")
}
Is there a recommended way of doing this? The second option seems more "Swifty"...
Neither is inherently better or Swiftier.
Personally I find throws initializers a huge pain. I'd much rather have a failed initializer return nil, because then I can do my initialization with guard let instead of having to wrap things in do/catch and deal with the resulting scoping issues. Your code illustrates the problem; your var d is "stuck" inside a do scope. I'd rather say this:
guard let d = Thing(c:-4) else {return}
// now d is unwrapped and in scope!
...than this (what you have to say):
do {
var d = try Thing(c: -4)
} catch {
print("oh vey!")
}
// and here there is no `d`, so _now_ what?
On the other hand, throwing an error offers an opportunity to send a message, i.e. to be communicative about exactly what went wrong. You can't do that with a mere init? initializer; it works or it fails, and that's all the caller knows.
In some programming language there is two other operator in addition with simple || and &&. these operators which I am going to call them _orif and _andif from now, can be used in place of && and || and They may help to improve efficiency and avoid errors, because evaluation of the conditional stops as soon as the answer is clear.
For example, evaluation of the following expression will stop halfway through (selectedSprite != nil) is false: So the rest of the conditional will be ignored and never evaluated, this will prevent a fatal error in this case : fatal error: unexpectedly found nil while unwrapping an Optional value and it will raise while reaches to the second expression because obviously nil does not responds to SpriteOwner().
if (selectedSprite != nil) &&
(selectedSprite.SpriteOwner().type == "Human")
{
println("a human selected")
}
I am looking for a replacement for && in above piece of code that could be used instead of the simple && operator, So if the first expression is evaluated as a false one (having the selectedSprite equal to nil) then the second expression be ignored at all.(since it does not have any influence on result)
Question:
Is there such a &&? operator in swift? if the answer is a No,
Is there a better way of doing that instead of nested if statements like I have written here :
if (selectedSprite != nil)
{
if (selectedSprite.SpriteOwner().type == "Human")
{
println("a human selected")
}
}
I am implementing an intelligent system with a lot of if clause in it and most of them are too complicated which adding a new if layer just to control nils is a real nightmare.
What you described is called short circuiting and Swift does have it. For example:
let a : Int? = nil
if a != nil && a! == 1 {
print("a is 1")
} else {
print("a is nil")
}
You can see a is never unwrapped. I think in your case, it's more likely that SpriteOwner() returns nil. the Swifty way to unwrap optional values is to use the let ... where ... syntax:
if let s = selectedSprite where s.SpriteOwner().type == "Human" {
println("a human selected")
}
The binary logical || and && operators in Swift 2 already behave as you describe. See "The Swift Programming Language", under "Logical AND Operator" : https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID60
If either value is false, the overall expression will also be false. In fact, if the first value is false, the second value won’t even be evaluated, because it can’t possibly make the overall expression equate to true. This is known as short-circuit evaluation.
Try executing the following in a playground; you will see that the second part of the conditional statement is never executed since the first part is false:
func isBar(rv: Bool) -> Bool {
print("side effect of isBar")
return rv
}
func isFoo() -> Bool {
print("side effect of isFoo")
return true
}
if isBar(false) && isFoo() {
print("both true")
}
You could achieve something similar with optional chaining:
class Sprite {
let otherSprite: Sprite?
let type: String
init(otherSprite: Sprite?, type: String) {
self.otherSprite = otherSprite
self.type = type
}
}
let innerSprite = Sprite(otherSprite: nil, type: "Human")
let outerSprite = Sprite(otherSprite: innerSprite, type: "Robot")
This doesn't execute the print statement because they otherSprite? comes back with a nil
if innerSprite.otherSprite?.type == "Human" {
print("A human selected")
}
This will print "A human selected" because the value of otherSprite is non-nil and the type's value == "Human"
if outerSprite.otherSprite?.type == "Human" {
print("A human selected")
}