Swift: "Where" vs "If" - swift

Is there any difference between these two syntaxes? If not, any benefit?
if let userName = userNameTextField.text where userName.characters.count > 0,
let password = passwordTextField.text where password.characters.count > 0,
let confirmation = confirmationTextField.text where confirmation == password
else {
return false
}
and:
if userNameTextField.text?.characters.count > 0 &&
passwordTextField.text?.characters.count > 0 &&
confirmationTextField.text == passwordTextField.text
{
return false
}

First of all, note that where clauses in optional binding conditions is deprecated in Swift 3.0, replaced by ,.
Evolution Proposal SE-0099: Restructuring Condition Clauses
I.e.
let opt: Int?
/* Swift < 3 */
if let opt = opt where opt == 42 { /* ... */ }
/* Swift >= 3 */
if let opt = opt, opt == 42 { /* ... */ }
Secondly, your second example block wont compile in Swift >= 3.0, as the optional result from the optional chaining is not unwrapped; comparing optionals to literals has been removed in Swift 3.0 (thanks #MartinR), as per the following implemented proposals:
Evolution proposal SE-0121: Remove Optional Comparison Operators
Evolution proposal SE-0123: Disallow coercion to optionals in operator arguments
userNameTextField.text?.characters.count > 0 &&
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
this is an optional that, for Swift >= 3.0, needs to be unwrapped
prior to comparing it to the integer literal */
Now, proceeding to answer your "is there any difference ..." question, assuming we look at your two options fixed for Swift 3.
You could choose to bind the value of the optional String property text of userNameTextField if you'd like to use it in the if block that follows, or
If youd only want to ascertain that the text property is not nil or empty (""), you could omit the binding in favour or simply checking its character.count
E.g.:
struct Foo {
var bar: String?
}
var foo = Foo()
/* if you want to use foo.bar within the if block */
if let str = foo.bar, str.characters.count > 0 {
// do something with str ...
print(str)
}
/* if you only need to ascertain foo.bar is not nil
and not empty */
if (foo.bar?.characters.count ?? 0) > 0 {
// do something with str ...
print("not empty")
}
// alternatively ... (not nil or empty)
if !(foo.bar?.isEmpty ?? true) {
// do something with str ...
print("not empty")
}
If you simply want to ascertain the latter and return false in case the ascertation fails, you could prefer using a guard statement instead of if:
guard (foo.bar?.characters.count ?? 0) > 0 else { return false }
// ... if no false return, proceed with app flow here ...
// alternatively ...
guard !(foo.bar?.isEmpty ?? true) else { return false }

Neither version works in Swift 3. And I think in the first version, you meant to use guard instead of let.
With Swift 3, you want to do the following:
guard
let userName = userNameTextField.text,
let password = passwordTextField.text,
let confirmation = confirmationTextField.text,
userName.characters.count > 0,
password.characters.count > 0,
confirmation == password
else {
return false
}

There are problems with using your approach in Swift 3 as explained by some of the other answers. But if you are using Swift 2, the following is my answer to your question.
The two are exactly the same with only one difference. Both approaches are testing for a nil value and then introducing a boolean test if the value is not nil. For example in your line,
if let userName = userNameTextField.text where userName.characters.count > 0
You are testing to see if userNameTextField is not nil and then you are testing if its count is greater than zero. The same thing applies this line,
if userNameTextField.text?.characters.count > 0
except it is much shorter and readable.
Because both approaches are within an 'if' statement, they both evaluate to true or false and achieve the same purpose.
However, what separates them is that with the 'where' clause, you can introduce a Boolean test after a binding or pattern, regardless of whether or not there's an underlying semantic link between the two. So I can basically do this with the 'where' clause,
if let userName = userNameTextField.text where age > 0
There is no semantic link between userNameTextField and age. As you can imagine, that might not be what you intended and this can quickly lead to errors.

Related

Combining optional chaining and ifs

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.

Verify multiple conditions with contains(where:)

With this code I enable a button if any form value is not nil:
myButton.isEnabled = !myForm.values.contains(where: { $0 == nil })
Is there any way to add multiple checks? I want to enable my button:
if the value of any field is nil (done, as you can see above);
if the value of any field is > 0;
if the value of any field is < 2;
(that is, the value should NOT be nil and should NOT be outside the range 0.0 - 2.0)
Can I still use contains(where:) to do it?
MyForm is a dictionary like this one:
["oct": nil,
"jan": Optional(3666.0),
"nov": nil,
"apr": nil,
"sep": nil,
"feb": nil,
"jul": nil,
"mar": nil,
"dec": nil,
"may": nil,
"jun": nil,
"aug": nil]
(strange enough, due to the behavior of a 3rd party library, it accepts nil values)
Yes, you can. If you want to enable the button if there is at least one Double value in range 0.0..<2.0 you don't need to call values. You can call contains on the dictionary directly. The where keyword is syntactic sugar and can be omitted when using trailing closure syntax.
The code uses the pattern matching operator to check the range.
myButton.isEnabled = myForm.contains { (_, value) in
guard let value = value as? Double else { return false }
return 0.0..<2.0 ~= value
}
If you want to enable the button if all values are Double in the given range you have to check inverted
myButton.isEnabled = !myForm.contains { (_, value) in
guard let value = value as? Double else { return true }
return !(0.0..<2.0 ~= value)
}
You can do it just by filtering all values like this:
myButton.isEnabled = !myForm.values.filter {
if let value = $0 { return value.field > 0.0 && value.field < 2.0 }
return false
}.count > 0
contains function is not appropriate solution in your case, because it should be used when you need to check if array contain some specific element. But in your case you need to check if values is in specific range.
It is possible to use contains function but it is not correct way

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

unwrapping multiple optionals in if statement

I want to unwrap two optionals in one if statement, but the compiler complaints about an expected expression after operator at the password constant.
What could be the reason?
if let email = self.emailField?.text && let password = self.passwordField?.text
{
//do smthg
}
Done in Swift.
Great news. Unwrapping multiple optionals in a single line is now supported in Swift 1.2 (XCode 6.3 beta, released 2/9/15).
No more tuple/switch pattern matching needed. It's actually very close to your original suggested syntax (thanks for listening, Apple!)
if let email = emailField?.text, password = passwordField?.text {
}
Another nice thing is you can also add where for a "guarding condition":
var email: String? = "baz#bar.com"
var name: String? = "foo"
if let n = name, e = email where contains(e, "#") {
println("name and email exist, email has #")
}
Reference: XCode 6.3 Beta Release Notes
Update for Swift 3:
if let email = emailField?.text, let password = passwordField?.text {
}
each variable must now be preceded by a let keyword
How about wrapping the optionals in a tuple and using switch to pattern match?
switch (self.emailField?.text, self.passwordField?.text) {
case let (.Some(email), .Some(password)):
// unwrapped 'email' and 'password' strings available here
default:
break
}
It's definitely a bit noisier, but at least it could also be combined with a where clause as well.
The usage
if let x = y {
}
is not equivalent to
if (let x = y) { // this is actually not allowed
}
"if let" is effectively a two-word keyword, which is equivalent to
if y != nil {
let x = y!
// rest of if let block
}
Before Swift 1.2
Like #James, I've also created an unwrap function, but this one uses the existing if let for control flow, instead of using a closure:
func unwrap<T1, T2>(optional1: T1?, optional2: T2?) -> (T1, T2)? {
switch (optional1, optional2) {
case let (.Some(value1), .Some(value2)):
return (value1, value2)
default:
return nil
}
}
This can be used like so:
if let (email, password) = unwrap(self.emailField?.text, self.passwordField?.text)
{
// do something
}
From: https://gist.github.com/tomlokhorst/f9a826bf24d16cb5f6a3
Note that if you want to handle more cases (like when one of the two fields is nil), you're better off with a switch statement.
Swift 4
if let suggestions = suggestions, let suggestions1 = suggestions1 {
XCTAssert((suggestions.count > suggestions1.count), "TEST CASE FAILED: suggestion is nil. delete sucessful");
}
I can't explain why the above code doesn't work, but this would be good a replacement:
if let email = self.emailField?.text
{
if let password = self.passwordField?.text
{
//do smthg
}
}
Based on #Joel's answer, I've created a helper method.
func unwrap<T, U>(a:T?, b:U?, handler:((T, U) -> ())?) -> Bool {
switch (a, b) {
case let (.Some(a), .Some(b)):
if handler != nil {
handler!(a, b)
}
return true
default:
return false
}
}
// Usage
unwrap(a, b) {
println("\($0), \($1)")
}

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.