if else shorthand crashes on setting bool in swift - ios

I literally have 4 line of code.
var bool = true
let textField1 = UITextField()
let textField2 = UITextField()
bool ? textField1.enabled = false : textField2.enabled = false
The above code fails with following error.
if I write the following way the code works
if bool {
textField1.enabled = false
}
else {
textfield2.enabled = false
}
If I write the following way then short hand of if else works
bool ? print("It's True") : print("It's False")
Why is my code failing?

The reason is that Swift is not expecting you to behave like this. Thus, it sees the second term in the ternary as textField2.enabled, which is a Bool. But the first term, textField1.enabled = false, is not a Bool; it is a Void.
(That is why your print example works; both terms are Voids.)
As i_am_jorf says, you can work around this by disambiguating with parentheses.
However, it would be better not to do this at all. Your code is not very Swifty. You should not be using the ternary operator for side effects in this way. You should be using it for the result of each of its terms. This is much cleaner and even shorter:
(bool ? textField1 : textField2).enabled = false

Try this:
bool ? (textField1.enabled = false) : (textField2.enabled = false)
Note that the ternary operator is not an "if-else shorthand". It is defined as:
The ternary conditional operator evaluates to one of two given values based on the value of a condition. It has the following form:
condition ? expression used if true : expression used if false
If the condition evaluates to true, the conditional operator evaluates the first expression and returns its value. Otherwise, it evaluates the second expression and returns its value. The unused expression is not evaluated.
It's purpose is to assign a value based on a condition, not to allow flow control.

Related

Using variable as Boolean in conditional statement in Dart

I'm learning Dart after coming from Python and I wanted to know what is the closest Dart has to using a non-Boolean variable as a Boolean in conditional statements. Like using a string where an empty string is false and a non-empty string is true.
For example, in Python:
name = 'Yes'
print('True' if name else 'False') // 'True'
name2 = ''
print('True' if name else 'False') // 'False'
Does Dart have something similar without having to convert the variable to a Boolean statement?
Dart has no affordance for using non-boolean values in tests. None, whatsoever. Can't be done.
The only expressions allowed in a test positions are those with static type:
bool
dynamic (which is implicitly downcast to bool as if followed by as bool, which throws at runtime if it's not actually a bool.)
Never, which always throws before producing a value.
The bool type, in non-null-safe code can evaluate to null. The test also throws at runtime if that happens.
So, for any test, if the test expression does not evaluate to a bool, it throws. You can only branch on an actual bool true or false.
If you have a value where you want to treat both null and empty as false, I'd do: if (value?.isEmpty ?? false) ...
I don't think such code will even compile. Type checking in dart tends to be very strict, and whenever the compile tries to evaluate a condition which is not of type bool it raises an error.
For example, the following won't compile:
final String value = "truthyValue" ? "truth" : "dare";
Instead, dart offers the isNonEmpty and isEmpty methods on many built in types such as strings, different containers so on, so you could do something like someString.isNotEmpty ? "good" : "bad";.
You can write extension on any types, if you need to handle the specific values such as '0' or '00000'. I like it very much because it is handy for any parsing and if you name it well it is readable.
extension BoolTest on String? {
bool get toBool {
if (this == null) {
return false;
} else {
return true;
}
}
}
then you simply use it like this:
String? a = 'hello';
String? b;
print(a.toBool); // true
print(b.toBool); // false
print('hello'.toBool) // true
Have a look at: https://dart.dev/guides/language/extension-methods

Operator Priority in Swift

I just spent a frustrating few hours trying to debug code some, through a process of elimination I was able to fix the problem but I can't figure out the cause and its bugging me just as much.
So a bit of context: we want to filter an array of objects (which we will just refer to as type Object) based on a collection of predicates of type Object -> Bool. We first combine all the predicates into a composed function and then use the filter function with our composed predicate to get the filtered array of objects. So heres an example:
var predicateA: (Object -> Bool) = { (obj) in obj.isFoo }
var predicateB: (Object -> Bool) = { (obj) in obj.isBar }
var composedPredicate: (Object -> Bool) {
return { (obj) in
return predicateA(obj) && predicateB(obj)
}
}
The above example works perfectly but when try to introduce an optional for the predicates, it doesn't work as expected.
var predicateA: (Object -> Bool) = { (obj) in obj.isFoo }
var optionalPredicateB: (Object -> Bool)? = nil
var composedPredicate: (Object -> Bool) {
return { (obj) in
return predicateA(obj)
&& optionalPredicateB != nil ? optionalPredicateB!(obj) : true
}
}
So the code above will cause the composedPredicate to always return true even if the one of the "sub-predicates" return false.
This can be fixed by changing the optionPredicateLine to include parans
&& (optionalPredicateB != nil ? optionalPredicateB!(obj) : true)
EDIT:
I believe the error is due to the priority on the operators in the ternary operator i.e. it is evaluating all the && statements before the ? in the ternary operator when determining which case to use.
Although I'm not 100% sure on this so i am looking for clarification / or if the cause is something else.
You can find the precedence of the operators here. It shows "&&" has a precedence of 120, whereas "?:" has a precedence of 100, and "!=" is 130. Therefore the "&&" is being evaluated before the ternary operator, which is the equivalent of:
return (predicateA(obj) && (optionalPredicateB != nil)) ? optionalPredicateB!(obj) : true
You can find the Swift list of infix operators here. They are ordered by decreasing order of precedence.
In this line
return predicateA(obj)
&& optionalPredicateB != nil ? optionalPredicateB!(obj) : true
Compiler is taking
predicateA(obj)
&& optionalPredicateB != nil
as a condition for ternary that's why it was not working so now you added braces so it is working fine

Filter an array based on empty value in Swift

I am trying to filter an array of dictionaries. The below code is the sample of the scenario i am looking for
let names = [
[ "firstName":"Chris","middleName":"Alex"],
["firstName":"Matt","middleName":""],
["firstName":"John","middleName":"Luke"],
["firstName":"Mary","middleName":"John"],
]
The final result should be an array for whom there is a middle name.
This did the trick
names.filter {
if let middleName = $0["middleName"] {
return !middleName.isEmpty
}
return false
}
You can also use the nil-coalescing operator to express this quite succinctly:
let noMiddleName = names.filter { !($0["middleName"] ?? "").isEmpty }
This replaces absent middle names with empty strings, so you can handle either using .isEmpty (and then negate if you want to fetch those with middle names).
You can also use optional chaining and the nil-coalescing operator to express it another way:
let noMiddleName = names.filter { !($0["middleName"]?.isEmpty ?? true) }
$0["middleName"]?.isEmpty will call isEmpty if the value isn’t nil, but returns an optional (because it might have been nil). You then use ?? to substitute true for nil.
Slightly shorter:
let result = names.filter { $0["middleName"]?.isEmpty == false }
This handles all three possible cases:
If the middle name exists and is not an empty string, then
$0["middleName"]?.isEmpty evaluates to false and the predicate
returns true.
If the middle name exists and is empty string, then
$0["middleName"]?.isEmpty evaluates to true and the predicate
returns false.
If the middle name does not exist, then
$0["middleName"]?.isEmpty evaluates to nil and the predicate
returns false (because nil != false).
This also works fine
names.filter {
if let middleName = $0["middleName"] {
return middleName != ""
}
return false
}

Swift: Testing optionals for nil

I'm using Xcode 6 Beta 4. I have this weird situation where I cannot figure out how to appropriately test for optionals.
If I have an optional xyz, is the correct way to test:
if (xyz) // Do something
or
if (xyz != nil) // Do something
The documents say to do it the first way, but I've found that sometimes, the second way is required, and doesn't generate a compiler error, but other times, the second way generates a compiler error.
My specific example is using the GData XML parser bridged to swift:
let xml = GDataXMLDocument(
XMLString: responseBody,
options: 0,
error: &xmlError);
if (xmlError != nil)
Here, if I just did:
if xmlError
it would always return true. However, if I do:
if (xmlError != nil)
then it works (as how it works in Objective-C).
Is there something with the GData XML and the way it treats optionals that I am missing?
In Xcode Beta 5, they no longer let you do:
var xyz : NSString?
if xyz {
// Do something using `xyz`.
}
This produces an error:
does not conform to protocol 'BooleanType.Protocol'
You have to use one of these forms:
if xyz != nil {
// Do something using `xyz`.
}
if let xy = xyz {
// Do something using `xy`.
}
To add to the other answers, instead of assigning to a differently named variable inside of an if condition:
var a: Int? = 5
if let b = a {
// do something
}
you can reuse the same variable name like this:
var a: Int? = 5
if let a = a {
// do something
}
This might help you avoid running out of creative variable names...
This takes advantage of variable shadowing that is supported in Swift.
Swift 3.0, 4.0
There are mainly two ways of checking optional for nil. Here are examples with comparison between them
1. if let
if let is the most basic way to check optional for nil. Other conditions can be appended to this nil check, separated by comma. The variable must not be nil to move for the next condition. If only nil check is required, remove extra conditions in the following code.
Other than that, if x is not nil, the if closure will be executed and x_val will be available inside. Otherwise the else closure is triggered.
if let x_val = x, x_val > 5 {
//x_val available on this scope
} else {
}
2. guard let
guard let can do similar things. It's main purpose is to make it logically more reasonable. It's like saying Make sure the variable is not nil, otherwise stop the function. guard let can also do extra condition checking as if let.
The differences are that the unwrapped value will be available on same scope as guard let, as shown in the comment below. This also leads to the point that in else closure, the program has to exit the current scope, by return, break, etc.
guard let x_val = x, x_val > 5 else {
return
}
//x_val available on this scope
One of the most direct ways to use optionals is the following:
Assuming xyz is of optional type, like Int? for example.
if let possXYZ = xyz {
// do something with possXYZ (the unwrapped value of xyz)
} else {
// do something now that we know xyz is .None
}
This way you can both test if xyz contains a value and if so, immediately work with that value.
With regards to your compiler error, the type UInt8 is not optional (note no '?') and therefore cannot be converted to nil. Make sure the variable you're working with is an optional before you treat it like one.
From swift programming guide
If Statements and Forced Unwrapping
You can use an if statement to find out whether an optional contains a
value. If an optional does have a value, it evaluates to true; if it
has no value at all, it evaluates to false.
So the best way to do this is
// swift > 3
if xyz != nil {}
and if you are using the xyz in if statement.Than you can unwrap xyz in if statement in constant variable .So you do not need to unwrap every place in if statement where xyz is used.
if let yourConstant = xyz {
//use youtConstant you do not need to unwrap `xyz`
}
This convention is suggested by apple and it will be followed by devlopers.
Although you must still either explicitly compare an optional with nil or use optional binding to additionally extract its value (i.e. optionals are not implicitly converted into Boolean values), it's worth noting that Swift 2 has added the guard statement to help avoid the pyramid of doom when working with multiple optional values.
In other words, your options now include explicitly checking for nil:
if xyz != nil {
// Do something with xyz
}
Optional binding:
if let xyz = xyz {
// Do something with xyz
// (Note that we can reuse the same variable name)
}
And guard statements:
guard let xyz = xyz else {
// Handle failure and then exit this code block
// e.g. by calling return, break, continue, or throw
return
}
// Do something with xyz, which is now guaranteed to be non-nil
Note how ordinary optional binding can lead to greater indentation when there is more than one optional value:
if let abc = abc {
if let xyz = xyz {
// Do something with abc and xyz
}
}
You can avoid this nesting with guard statements:
guard let abc = abc else {
// Handle failure and then exit this code block
return
}
guard let xyz = xyz else {
// Handle failure and then exit this code block
return
}
// Do something with abc and xyz
Swift 5 Protocol Extension
Here is an approach using protocol extension so that you can easily inline an optional nil check:
import Foundation
public extension Optional {
var isNil: Bool {
guard case Optional.none = self else {
return false
}
return true
}
var isSome: Bool {
return !self.isNil
}
}
Usage
var myValue: String?
if myValue.isNil {
// do something
}
if myValue.isSome {
// do something
}
One option that hasn't specifically been covered is using Swift's ignored value syntax:
if let _ = xyz {
// something that should only happen if xyz is not nil
}
I like this since checking for nil feels out of place in a modern language like Swift. I think the reason it feels out of place is that nil is basically a sentinel value. We've done away with sentinels pretty much everywhere else in modern programming so nil feels like it should go too.
Instead of if, ternary operator might come handy when you want to get a value based on whether something is nil:
func f(x: String?) -> String {
return x == nil ? "empty" : "non-empty"
}
Another approach besides using if or guard statements to do the optional binding is to extend Optional with:
extension Optional {
func ifValue(_ valueHandler: (Wrapped) -> Void) {
switch self {
case .some(let wrapped): valueHandler(wrapped)
default: break
}
}
}
ifValue receives a closure and calls it with the value as an argument when the optional is not nil. It is used this way:
var helloString: String? = "Hello, World!"
helloString.ifValue {
print($0) // prints "Hello, World!"
}
helloString = nil
helloString.ifValue {
print($0) // This code never runs
}
You should probably use an if or guard however as those are the most conventional (thus familiar) approaches used by Swift programmers.
Optional
Also you can use Nil-Coalescing Operator
The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.
let value = optionalValue ?? defaultValue
If optionalValue is nil, it automatically assigns value to defaultValue
Now you can do in swift the following thing which allows you to regain a little bit of the objective-c if nil else
if textfieldDate.text?.isEmpty ?? true {
}
var xyz : NSDictionary?
// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary()
// case 3: do nothing
if xyz { NSLog("xyz is not nil.") }
else { NSLog("xyz is nil.") }
This test worked as expected in all cases.
BTW, you do not need the brackets ().
If you have conditional and would like to unwrap and compare, how about taking advantage of the short-circuit evaluation of compound boolean expression as in
if xyz != nil && xyz! == "some non-nil value" {
}
Granted, this is not as readable as some of the other suggested posts, but gets the job done and somewhat succinct than the other suggested solutions.
If someone is also try to find to work with dictionaries and try to work with Optional(nil).
let example : [Int:Double?] = [2: 0.5]
let test = example[0]
You will end up with the type Double??.
To continue on your code, just use coalescing to get around it.
let example : [Int:Double?] = [2: 0.5]
let test = example[0] ?? nil
Now you just have Double?
This is totally logical, but I searched the wrong thing, maybe it helps someone else.
Since Swift 5.7:
if let xyz {
// Do something using `xyz` (`xyz` is not optional here)
} else {
// `xyz` was nil
}

Single line if statement in Swift

How would one convert the following to Swift from Objective-C?
if (myVar) return;
Swift does not use parentheses around the conditional, however the following code gives an error.
if myVar return
Well as the other guys also explained that braces are a must in swift. But for simplicity one can always do something like:
let a = -5
// if the condition is true then doThis() gets called else doThat() gets called
a >= 0 ? doThis(): doThat()
func doThis() {
println("Do This")
}
func doThat() {
println("Do That")
}
In Swift the braces aren't optional like they were in Objective-C (C). The parens on the other hand are optional. Examples:
Valid Swift:
if someCondition {
// stuff
}
if (someCondition) {
// stuff
}
Invalid Swift:
if someCondition
// one liner
if (someCondition)
// one liner
This design decision eliminates an entire class of bugs that can come from improperly using an if statement without braces like the following case, where it might not always be clear that something's value will be changed conditionally, but somethingElse's value will change every time.
Bool something = true
Bool somethingElse = true
if (anUnrelatedCondition)
something = false
somethingElse = false
print something // outputs true
print somethingElse // outputs false
You can use new Nil-Coalescing Operator, since Swift 3 in case when you need just set default value in case of if fails:
let someValue = someOptional ?? ""
In case if someOptional is false, this operator assign "" to someValue
var dataDesc = (value == 7) ? "equal to 7" : "not equal to 7"
One-line if, one-line while and one-line for are considered a bad style by many developers because they are less readable and allegedly a source of many errors.
Swift solved the conundrum by forbidding one-line flow control statements; the braces are non-optional...
if someCondition {
// stuff
}
Of course, you can still do
if someCondition { return }
There are also implementation reasons. Having the parentheses around the condition as optional makes the parsing much harder. Enforcing braces simplifies parsing again.
Here is a simple solution I used in my projects.
Swift 4+
isManageCardTnCSelected ?
(checkbox.isSelected = true) : (checkbox.isSelected = false)
var selected: Bool = isManageCardTnCSelected ?
checkbox.isSelected = true : checkbox.isSelected = false
Swift 3+
var retunString = (state == "OFF") ? "securityOn" : "securityOff"
One line solution for Swift 3+
isMyVar ? someAction() : ()
Swift 5 Easy One line Solution
var isSeller = (UserDefaults.standard.value(forKey: "isRole") as? String == "ROLE_SELLER") ? true : false
Swift 5 Easy Solution
let exercise = (segcExercise.selectedSegmentIndex == 0) ? "GymLover" : (segcExercise.selectedSegmentIndex == 1) ? "Occasionally" : "No Way"
//MARK:- outPut // Occasionally

Resources