Operator Priority in Swift - ios

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

Related

Try to return in ternary expression

I have a Swift application.
I'm getting the error Expected expression after '?' in ternary expression from Xcode compiler
in
private func getContentPre(highlight: Highlight) -> String!
{
highlight.contentPre.count == 0 ? return ">" : return highlight.contentPre
}
Apple docs says:
why isn't it possible to return in ternary expression like using if statement?
You should re-write your function like this. This will evaluate the count of the contentPre variable and return the appropriate response.
private func getContentPre(highlight: Highlight) -> String! {
return highlight.contentPre.count == 0 ? ">" : highlight.contentPre
}
However as it would appear that contentPre would be a String you should use .isEmpty as it is more performant that checking the length of a String
private func getContentPre(highlight: Highlight) -> String! {
return highlight.contentPre.isEmpty ? ">" : highlight.contentPre
}
return does not return anything - I mean, to the function what calls it. Ternary operator's parameters must be expressions.

Use question mark operator when calling method on possible null value?

I have a varible containing an object which might be null. When trying to call a method I have to check for null. I want the result to be false if the variable is null. What is considered good and readable style to to this?
e.g.
class MyClass {
bool boolMethod() {
return true;
}
}
void main() {
MyClass mc = new MyClass();
MyClass mcnull = null;
bool val1 = mc?.boolMethod();
bool val1null = mcnull?.boolMethod();
bool val2 = mc != null && mc.boolMethod();
bool val2null = mcnull != null && mcnull.boolMethod();
}
Especially when used in if-statements I consider the first version much more readable:
if (mc?.boolMethod())...
versus
if (mc != null && mc.boolMethod())...
But IntelliJ gives me the hint The value of the '?.' operator can be 'null' which isn't appropriate as an operand of a locaical operator. (null_aware_in_logical_operator). Ok - this is right because when the variable is null then I use the null as a boolean value. But it's ok in my case and I try to avoid suppressing warnings.
What is the suggested way? Other ideas?
I think a common pattern is
bool val1 = (mc?.boolMethod() ?? false);
The parens aren't required here but if such expressions are embedded into more complex expressions they are often necessary to get the expected behavior because of the low priority of ??

For loop update for Swift 2.2 throwing Binary Operator error

I'm trying to update some of my swift code to comply with the swift 2.2 guide lines. I'm trying to update my for loops this is what I have currently
for(var i = 0; persons?.count > i; i += 1){}
and this is what I thought I should be using
for i in (0..<persons?.count){}
But I'm getting this error "Binary operator '..<' cannot be applied to operands of type 'Int' and 'Int?'"
I'm just not sure what I'm missing.
The problem is that persons?.count might be nil and 0..<nil doesn't make any sense. you can easily fix this by using nil coalescing though:
for i in 0..<(persons?.count ?? 0) {
doStuff(i)
}
Or if you prefer you could overload the ..< operator to accept an optional as its second argument:
func ..<<T:ForwardIndexType where T:Comparable>(lower: T, upper: T?) -> Range<T> {
return lower..<(upper ?? lower)
}
This allows you to just write:
for i in 0..<persons?.count {
doStuff(i)
}
As Sulthan points out in the comments, this is probably not the best solution for your problem though. You should likely handle the case of persons being nil earlier in your code:
guard let persons = persons else {
// take care of business
}
for person in persons {
doStuff(person)
}

Generics with Generators and Sequences in Swift

I have a fairly good understanding of OO programming and Swift, however, one area that really leaves me stumped is Generators and Sequences (I am fine with the concept of protocols by the way).
For example, I completed this exercise from the Swift Guide (Apple)
“EXPERIMENT Modify the anyCommonElements function to make a function that returns an array of the elements that any two sequences have in common.”
Turning this:
func​ ​anyCommonElements​ <​T​, ​U​ ​where​ ​T​: ​SequenceType​, ​U​: ​SequenceType​, ​T​.​Generator​.​Element​: ​Equatable​, ​T​.​Generator​.​Element​ == ​U​.​Generator​.​Element​> (​lhs​: ​T​, ​rhs​: ​U​) -> ​Bool​ {
for​ ​lhsItem​ ​in​ ​lhs​ {
for​ ​rhsItem​ ​in​ ​rhs​ {
if​ ​lhsItem​ == ​rhsItem​ {
return​ ​true
}
}
}
return​ ​false
}
Into this:
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element]? {
var commonElements:[T.Generator.Element]? = nil
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
if (commonElements == nil)
{
commonElements = [] //init the array if not already
}
commonElements?.append(lhsItem) //add matching item to the array
}
}
}
return commonElements? //return nil or array of matched elements
}
I'm happy with the solution I've written and it works well, including the Optional return, however I am lost as to why the type of the commonElements return array needs to be this:
var commonElements:[T.Generator.Element]
Rather than this:
var commonElements:[T]
I've read a fair bit on the subject, including:
https://schani.wordpress.com/2014/06/06/generators-in-swift/
http://robots.thoughtbot.com/swift-sequences
http://natashatherobot.com/swift-conform-to-sequence-protocol/
But I am still completely lost - can someone please help explain this in simple terms or is it just a little abstract and not easy to describe?
Would really appreciate it,
Thanks, John
Update for Swift 5:
(using Sequence instead of SequenceType, Iterator instead of Generator, and new Where syntax.
func anyCommonElements <T, U> (lhs: T, rhs: U) -> [T.Iterator.Element] where T: Sequence, U: Sequence, T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
var commonElements:[T.Iterator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
commonElements.append(lhsItem) //add matching item to the array
}
}
}
return commonElements //return nil or array of matched elements
}
In fact, you can just do T.Element now, and forget about the Iterator.
T is a sequence type. For simplicity, let's take a special and familiar case and say T is an array.
Then the type of thing contained in the array is T.Generator.Element. This is because of the way the Array struct is defined. Keep in mind that Array is a generic. It is a SequenceType, which is a (generic) protocol with an empty type alias Generator, which is constrained to be a GeneratorType, which in turn is a (generic) protocol that has an empty type alias Element. When the generic is specialized, those empty type aliases are "filled in" with an actual type. All sequences are like that. So if T is an Array, then T.Generator.Element means "the actual type of this array's actual elements".
So [T.Generator.Element] means "an array of the same kind of element as the original array's elements.
Your proposed expression, [T], would mean an array of arrays, which is not what we want.
Okay, now generalize T back to any sequence (array, string of character, etc.) and that explanation keeps on working.
The author's answer is no longer valid for the latest version of Swift. Here is an update that is compatible with version 3.0.1, they have simplified how to make a generic array. note: I originally used [Any] arrays, but updated the code based on the feedback below.
func showCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
var result:[T.Iterator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
result.append(lhsItem)
}
}
}
return result
}
You can test the code with the following commands:
showCommonElements([1, 2, 3, 4, 5], [4, 7, 3])
showCommonElements(["apple", "banana", "orange", "peach"], ["orange", "pear", "apple"])

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