Combining optional chaining and ifs - swift

I found out, that I can write this code:
func optionalReturn() -> Int? {
// do sth. and return maybe an Int, otherwise:
return nil
}
if let x = optionalReturn(), x > 5 {
print("test exists and is greater then 5")
}
Now I'm standing in front of the following problem: I want to have an if, that handles two cases:
case 1: x is not existing, OR
case 2: x is existing, but is greater than value 5
This is not working:
if x == nil || (let x = optionalReturn(), x > 5) {
...
}
Is sth. like this possible in Swift?
UPDATE
The above code was simplified to show my problem, but my case is a little bit different, because my called function doesn't return an optional Int, it returns an optional struct:
struct TestStruct {
var x: Int
var y: Int
}
func optionalReturn() -> TestStruct? {
// do sth. and maybe return a TestStruct(....), otherwise:
return nil
}
let test = optionalReturn()
if test == nil || ...?
UPDATE 2
In my first update I had a mistake. Thanks to Christik, who mentioned it in the comments, my code had a mistake and would have worked without it. I accepted his answer as a solution, but my first idea was right as well: Swift skips the other if-conditions, if the first OR-condition is true. So this code works as well:
...
if test == nil || (test!.x > 5 && test!.y > 6) {
print("I am here")
}

You can use the nil coalescing operator here, and give x a value greater than 5, if its nil:
if (x ?? 6) > 5 {
// do your thing
}
In regards to the question update, you can use map on the optional to achieve your goal:
if x.map({ $0.x > 5 && $0.y > 6 }) ?? true {
// do your thing
}
map has the advantage that it avoids the forced unwrap.

Related

Separating multiple if conditions with commas in Swift

We already know multiple optional bindings can be used in a single if/guard statement by separating them with commas, but not with && e.g.
// Works as expected
if let a = someOpt, b = someOtherOpt {
}
// Crashes
if let a = someOpt && b = someOtherOpt {
}
Playing around with playgrounds, the comma-style format also seems to work for boolean conditions though I can't find this mentioned anywhere. e.g.
if 1 == 1, 2 == 2 {
}
// Seems to be the same as
if 1 == 1 && 2 == 2 {
}
Is this an accepted method for evaluating multiple boolean conditions, and is the behaviour of , identical to that of && or are they technically different?
Actually the result is not the same. Say that you have 2 statements in an if and && between them. If in the first one you create a let using optional binding, you won't be able to see it in the second statement. Instead, using a comma, you will.
Comma example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)), cell.isSelected {
//Everything ok
}
&& Example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)) && cell.isSelected {
//ERROR: Use of unresolved identifier 'cell'
}
Hope this helps.
In Swift 3, the where keyword in condition clauses were replaced by a comma instead.
So a statement like if 1 == 1, 2 == 2 {} is saying "if 1 equals 1 where 2 equals 2..."
It's probably easiest to read a conditional statement with an && instead of a ,, but the results are the same.
You can read more about the details of the change in Swift 3 in the Swift Evolution proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md
When it comes to evaluating boolean comma-separated conditions, the easies way to think of a comma is a pair or brackets.
So, if you have
if true, false || true {}
It gets transformed into
if true && (false || true) {}
Here is a case where they are sufficiently different as to require the ,. The following code will yield
'self' captured by a closure before all members were initialized
class User: NSObject, NSCoding {
public var profiles: [Profile] = []
private var selectedProfileIndex : Int = 0
public required init?(coder aDecoder: NSCoder) {
// self.profiles initialized here
let selectedIndex : Int = 100
if 0 <= selectedIndex && selectedIndex < self.profiles.count { <--- error here
self.selectedProfileIndex = selectedIndex
}
super.init()
}
...
}
This is due to the definition of && on Bool:
static func && (lhs: Bool, rhs: #autoclosure () throws -> Bool) rethrows -> Bool
The selectedIndex < self.profiles.count on the RHS is caught in a closure.
Changing the && to , will get rid of the error. Unfortunately, I'm not sure how , is defined, but I thought that this was interesting.
https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_condition-list
The Swift grammar says that the if statement condition-list can be composed by multiple condition separated by commas ,
A simple condition can be a boolean expression, a optional-binding-condition or other things:
So, using the comma to separate multiple expressions is perfectly fine.
When pattern matching a associated value in a switch, it matters when using a , or &&. One compiles, the other won't (Swift 5.1). This compiles (&&):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10 && integer < 10:
print("hi")
default:
fatalError()
}
}
This won't (,):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10, integer < 10:
print("hi")
default:
fatalError()
}
}

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.

Unwrapping optional inside of closure using reduce

I have a quick question that is confusing me a little bit. I made a simple average function that takes an array of optional Ints. I check to make sure the array does not contain a nil value but when I use reduce I have to force unwrap one of the two elements in the closure. Why is it that I only force unwrap the second one (in my case $1!)
func average2(array: [Int?]) -> Double? {
let N = Double(array.count)
guard N > 0 && !array.contains({$0 == nil}) else {
return nil
}
let sum = Double(array.reduce(0) {$0+$1!})
let average = sum / N
return average
}
I know it is simple but I would like to understand it properly.
The first parameter of reduce is the sum, which is 0 in the beginning. The second one is the current element of your array which is an optional Int and therefore has to be unwrapped.
Your invocation of reduce does this:
var sum = 0 // Your starting value (an Int)
for elem in array {
sum = sum + elem! // This is the $0 + $1!
}
EDIT: I couldn't get a more functional approach than this to work:
func average(array: [Int?]) -> Double? {
guard !array.isEmpty else { return nil }
let nonNilArray = array.flatMap{ $0 }
guard nonNilArray.count == array.count else { return nil }
return Double(nonNilArray.reduce(0, combine: +)) / Double(nonNilArray.count)
}
You can also discard the second guard if you want something like average([1, 2, nil]) to return 1.5 instead of nil

Working with optionals in Swift programming language

As far as I know the recommended way to use optionals (Int in this example) is the following:
var one:Int?
if var maybe = one {
println(maybe)
}
Is it possible to use a shorter way to do something like the following?
var one:Int?
var two:Int?
var three:Int?
var result1 = one + two + three // error because not using !
var result2 = one! + two! + three! // error because they all are nil
Update
To be more clear about what I'm trying to do: I have the following optionals
var one:Int?
var two:Int?
var three:Int?
I don't know if either one or two or three are nil or not. If they are nil, I wan't them to be ignored in the addition. If they have a value, I wan't them to be added.
If I have to use the recommended way I know, it would look something like this: (unnested)
var result = 0
if var maybe = one {
result += maybe
}
if var maybe = two {
result += maybe
}
if var maybe = three {
result += maybe
}
Is there a shorter way to do this?
Quick note - if let is preferred for optional binding - let should always be used where possible.
Perhaps Optionals aren't a good choice for this situation. Why not make them standard Ints with a default value of 0? Then any manipulation becomes trivial and you can worry about handling None values at the point of assignment, rather than when you're working on the values?
However, if you really want to do this then a tidier option is to put the Optionals into an Array and use reduce on it:
let sum = [one,two,three,four,five].reduce(0) {
if ($1) {
return $0 + $1!
}
return $0
}
That's exactly the point of optionals — they may be nil or non-nil, but unwrapping them when they're nil is an error. There are two types of optionals:
T? or Optional<T>
var maybeOne: Int?
// ...
// Check if you're not sure
if let one = maybeOne {
// maybeOne was not nil, now it's unwrapped
println(5 + one)
}
// Explicitly unwrap if you know it's not nil
println(5 + one!)
T! or ImplicitlyUnwrappedOptional<T>
var hopefullyOne: Int!
// ...
// Check if you're not sure
if hopefullyOne {
// hopefullyOne was not nil
println(5 + hopefullyOne)
}
// Just use it if you know it's not nil (implicitly unwrapped)
println(5 + hopefullyOne)
If you need to check multiple optionals at once here there are a few things you might try:
if maybeOne && maybeTwo {
println(maybeOne! + maybeTwo!)
}
if hopefullyOne && hopefullyTwo {
println(hopefullyOne + hopefullyTwo)
}
let opts = [maybeOne, maybeTwo]
var total = 0
for opt in opts {
if opt { total += opt! }
}
(It seems you can't use the let optional binding syntax with more than one optional at once, at least for now...)
Or for extra fun, something more generic and Swifty:
// Remove the nils from a sequence of Optionals
func sift<T, S: Sequence where S.GeneratorType.Element == Optional<T>>(xs: S) -> GeneratorOf<T> {
var gen = xs.generate()
return GeneratorOf<T> {
var next: T??
do {
next = gen.next()
if !next { return nil } // Stop at the end of the original sequence
} while !(next!) // Skip to the next non-nil value
return next!
}
}
let opts: [Int?] = [1, 3, nil, 4, 7]
reduce(sift(opts), 0) { $0 + $1 } // 1+3+4+7 = 15

How would I create a constant that could be one of several strings depending on conditions?

I want to have a constant using let that may be one of several values.
For instance:
if condition1 {
constant = "hi"
}
else if condition2 {
constant = "hello"
}
else if condition3 {
constant = "hey"
}
else if condition4 {
constant = "greetings"
}
I'm not sure how to do this with Swift and the let feature. But I'm inclined to believe it's possible, as this is in the Swift book:
Use let to make a constant and var to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
How would I accomplish this?
As pointed out in the other answers you can't directly do this. But if you're looking to just variably set the initial value of a constant, then yes, that is possible. Here's an example with a computed property.
class MyClass {
let aConstant: String = {
if something == true {
return "something"
} else {
return "something else"
}
}()
}
I think you are looking for variable which will be assigned later inside switch-case:
let constant :String
switch conditions {
case condition1:
constant = "hi"
case condition2:
constant = "hello"
case condition3:
constant = "hey"
case condition4:
constant = "greetings"
default:
constant = "salute"
}
One option would be something like this, using a closure:
let constant: String = ({ value in
if conditionOne {
return "Hi"
} else if conditionTwo {
return "Bye"
}
return "Oops!"
})(myData /*needed for condition*/)
Or, for another twist, using generics:
func fancySwitch<S, T>(val: S, fn: S -> T) -> T {
return fn(val)
}
let x: String = fancySwitch(3) { val in
if val == 2 {
return "Hi"
} else if val < 5 {
return "Bye"
}
return "Oops"
}
let y: String = fancySwitch((3, 4)) { (a, b) in
if a == 2 {
return "Hi"
} else if b < 5 {
return "Bye"
}
return "Oops"
}
I understand what you're looking for. In Scala and some other functional languages this can be done using the match statement (kind of like switch) because the entire statement resolves to a value like this:
val b = true
val num = b match {
case true => 1
case false => 0
}
This is unfortunately not directly possible in Swift because there is no way to get a value from a branch statement. As stated in the Swift book, "Swift has two branch statements: an if statement and a switch statement." Neither of these statements resolve to a value.
The closest code structure I can think of is to first use a variable to retrieve the correct value and then assign it to a constant to be used in any later code:
let b = true
var num_mutable: Int
switch b {
case true:
num_mutable = 1
default:
num_mutable = 0
}
let num = num_mutable
Just add the line let constant: String before your if/else statement.
Below, an excerpt from Swift 1.2 and Xcode 6.3 beta - Swift Blog - Apple Developer elaborates.
let constants are now more powerful and consistent — The new rule is
that a let constant must be initialized before use (like a var), and
that it may only be initialized, not reassigned or mutated after
initialization. This enables patterns like:
let x : SomeThing
if condition {
x = foo()
} else {
x = bar()
}
use(x)
This formerly required the use of a var even though there is no
mutation taking place. Properties have been folded into this model to
simplify their semantics in initializers as well.
I found the Swift blog post above from the article "Let It Go: Late Initialization of Let in Swift", which I found by googling: swift let constant conditional initialize.