How does Swift handle if statements? - swift

If I have the statement:
if object != nil && object.property == 2 {
//do something
}
does the if statement break as soon as it finds out object = nil?

Yes
When you concatenate a list of conditions C[0]...C[n] with the AND && operator, the runtime evaluates in order each condition and if a C[i] condition is found false, then the evaluation of the whole expression does end and it is judged false.
let c0 = true
let c1 = false
let c2 = true
if c0 && c1 && c2 {
print("Hello world")
}
In this case only c0 and c1 will be evaluated and the whole expression will be interpreted as false.
You can test it yourself in Playground.
c0 || c1 || c2
Symmetrically if you define an expression as the OR || concatenation of several clauses, then the whole expression is interpreted as true (and the evaluation of the clauses does stop) as soon as the first true condition gets found.

Yes is the answer.
Simple to try - see below - however note that as written your test won't work - a non-optional cannot be nil, and so you will have to unwrap it to test .property.
struct MyObj {
var property: Int
}
var object: MyObj? = nil
if object != nil && object!.property == 2 {
print("Not nil")
} else {
print("Nil") // Prints - would have crashed if test fully evaluated
}
object = MyObj(property: 2)
if object != nil && object!.property == 2 {
print("Not nil") // Prints
} else {
print("Nil")
}

Related

How to compare two string indexes in swift 4

I'm trying to find whether the character "S" or "C" appears in a string. At least one will be in the string but not necessarily both.
let S = codeZ.characters.index(of: "S")
let C = codeZ.characters.index(of: "C")
if (C == nil) || S < C {
nextView.sequential_flag = true
nextView.continuous_flag = false
}
else if S == nil || (C < S) {
nextView.sequential_flag = false
nextView.continuous_flag = true
}
I'm getting the error : Binary operator '<' cannot be applied to two 'String.CharacterView.Index?' (aka 'Optional') operands
In my experience with swift this usually means something else if wrong.
Also I've tried changing the if statements to this below.
if (C == nil) || S?.encodedOffset < C?.encodedOffset {
nextView.sequential_flag = true
nextView.continuous_flag = false
}
And I got the error : Binary operator '<' cannot be applied to two 'Int?' operands.
Any help on how to do this is much appreciated, thank you.
You should check if S is nil or not and provide a fallback value to C. Then you can compare the two non-optional values.
if let S = S, S.encodedOffset < (C?.encodedOffset ?? Int.max) {
nextView.sequential_flag = true
nextView.continuous_flag = false
}

Are "&&" and "," the same in Swift?

As the title says, I just came across a case where if && (AND) and , give the same result in Swift. I tested the code below:
let a = 5
let b = 6
if a<6, b>0 {
print("should be true")
}
if a<6, b<0 {
print("should be false")
}
if a>6, b>0 {
print("should be false")
}
if a>6, b<0 {
print("should be false")
}
It only logs:
should be true
So, the behavior of , is just like &&, am I correct?
They can be used in similar situations but that does not mean they are exactly the same.
Consider:
if (a && b) || c
you cannot write
if (a, b) || c
even a && b || c is different from a, b || c.
if a, b is something like
if a {
if b {
...
}
}
Both expressions have to be evaluated to true but they are still two separate expressions. We shouldn't imagine && there.
Why do we need the , operator?
The operator is needed to combine optional binding with boolean conditions, e.g.
if let a = a, a.isValid() {
becuase && wouldn't help us in such situations.
They're different in that a comma will take the lowest precedence possible. i.e everything else will be executed before the commas are checked.
// (true || false) && false
if true || false, false {
print("I will never be executed")
}
// true || (false && false)
if true || false && false {
print("I am always executed")
}

Swift - Unwrap optional in for in loop with where clause

I have a class with an optional member :
class A {
var i: Int? = nil
}
Then I have an array of objects of type A. Some objects in the array have a value for i, some others don't.
I want to iterate over objects in the array that have a value for i while unwrapping the optional at the same time. I didn't find a way to do both at the same time (I don't even know if it's possible), forcing me to write a if let construct inside the loop.
For example :
// a1, a2 have a value for i
let arr: [A] = [a1, a2, a3]
for obj in arr where obj.i != nil {
// I want to avoid if let, or force unwrapping here
if let unwrapped = obj.i {
print(i)
}
// let unwrapped = obj.i! ...
}
Is it possible in Swift ?
1.Maybe you can use flatMap to get value i, then print it
arr.flatMap{ $0.i }.forEach{ print($0) }
2.or Trying simple guard statement
arr.forEach { element in
guard let i = element.i else { return }
print(i)
}
I don't think that's possible.
Even if you have a where clause in your loop the type of obj is still of type A and as such i still remains optional.
To see why this is so think about the fact that you can change the value of i on object obj inside the loop, so the compiler is not sure that the value of i is valid until you unwrapp it.
You can try something like this
for obj in arr where obj.i != nil {
guard let i = obj.i else { continue }
print( i )
}
but if you start using guard you also skip the where clause
for obj in arr {
guard let i = obj.i else { continue }
print( i )
}
You can use case let syntax, but not without the help of map, and the result isn't the most readable:
for case let .some(unwrapped) in arr.map(\.i) {
print(unwrapped)
}
It's more useful if you're e.g. casting the outer object, e.g.:
for case let object as String in arrayOfAny {
if object.hasPrefix("tw") {
print("Starts with 'tw'")
}
}
instead of:
for object in arrayOfAny where object is String {
if object.hasPrefix("tw") { // Error: Value of type 'Any' has no member 'hasPrefix'
print("Starts with 'tw'")
}
}

Multiple conditions in guard statement in Swift

Is there a way to include multiple conditions in a guard statement of Swift?
For example, if I want to check two optional values are nil using a guard, how should I do it using single guard statement?
Check this code
func demo(){
var str = [String: String]()
str["status"] = "blue"
str["asd"] = nil
guard let var2 = str["asd"], let var1 = str["status"]
else
{
print("asdsfddffgdfgdfga")
return
}
print("asdasdasd")
}
Guard will check one by one condition. If the first is true then it will check the next. Otherwise, it will execute the else part.
To answer Prabhav's question, yes, you are correct, each condition in a guard statement must be true in order to proceed (i.e., not go into the else block). In this sense, it is indeed like separating conditions with AND logic.
You can implement OR logic, not by using commas, but by using a Boolean condition:
guard
true || false // this guard statement evaluates to true
else
{
print("no, not all values in the guard were true")
return
}
print("yes, all of the values in the guard were true") // this is printed
or a combination of OR and AND logic, by using a combination of Boolean conditions and optional bindings:
let testString: String? = nil
guard
true || false,
let x = testString, // this guard statement evaluates to false
true
else
{
print("no, not all values in the guard were true") // this is printed
return
}
print("yes, all of the values in the guard were true")
This summary from Apple, written about optional bindings in if statements is equally applicable to guard statements:
You can include as many optional bindings and Boolean conditions in a
single if statement as you need to, separated by commas. If any of the
values in the optional bindings are nil or any Boolean condition
evaluates to false, the whole if statement’s condition is considered
to be false. The following if statements are equivalent:
if let firstNumber = Int("4"), let secondNumber = Int("42"),
firstNumber < secondNumber && secondNumber < 100
{
print("\(firstNumber) < \(secondNumber) < 100")
} // Prints "4 < 42 < 100"
if let firstNumber = Int("4")
{
if let secondNumber = Int("42")
{
if firstNumber < secondNumber && secondNumber < 100
{
print("\(firstNumber) < \(secondNumber) < 100")
}
}
} // Prints "4 < 42 < 100"

Swift idiomatic way to guard both aren't nil

Lets say I have two variables, both optionals:
var a:Int? = 42
var b:Int? = 13
I have a condition where it's OK to proceed as long as these are not BOTH currently nil. I thoughtlessly put together something like:
guard let _ = a, let _ = b else { return }
I was absentmindedly thinking the conditions would be OR'ed, rather than AND'ed. Obviously that was wrong. The question then becomes, is there an idiomatic/preferred way to test that? Or do I just regress to the basics:
if a == nil && b == nil { return }
Aside
If I use the message extensions added by this post, then I might happily write something like
guard a.notNil || b.notNil else { return }
Which is about is close as I can come to "make certain (guard) that a is not nil or b is not nil"
A guard is an if, really, so you can do this the same way. This is clear and uses guard, which seems to be part of the fun. I present OR and AND options so you can pick one.
func doItOr(a: Int?, b:Int?) {
guard (a != nil || b != nil) else { return }
print("either A and B is not nil");
}
func doItAnd(a: Int?, b:Int?) {
guard (a != nil && b != nil) else { return }
print("both A and B are not nil");
}
doItOr(nil, b: nil)
doItOr(nil, b: 5)
doItOr(4, b: 5) // prints
doItAnd(nil, b: nil)
doItAnd(nil, b: 5)
doItAnd(nil, b: nil)
doItAnd(4, b: 5) // prints
it's OK to proceed as long as these are not BOTH currently nil
Your question poses two quite different possible conditions, so I will assume, for purposes of discussion, that you mean this one, namely "not both currently nil". Okay, then: "Not" is !. "Both" is &&. So, like this:
guard !(a == nil && b == nil) else {return}
If you really don't need to bind a or b as non-optionals or discern which of the two is non-nil, I believe the idiomatic approach would still be to use the if statement.
I generally use guard to narrow the specificity of my parameters by binding optionals, downcasting, etc.
You don't seem to be interested in doing that here. You just want to check them, so if seems to appropriately express that.