Why do you need an else if for Bronze instead of inline with the original if statement?

Hey there,

I solved the Bronze solution with this:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        
        let existingTextHasDecimalSeparator = textField.text?.range(of: ".")
        let replacementTextHasDecimalSeparator = string.range(of: ".")
        let letters = NSCharacterSet.letters
        
        if existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil {
            return false
        } else if string.rangeOfCharacter(from: letters) != nil {
            return false
        } else {
            return true
        }
    }

My question is, why couldn’t you avoid the else if and have the if statement be:

if existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil, string.rangeOfCharacter(from: letters) != nil

Xcode barks if you try to do this. Any ideas?

Given

func foo (_ x:Int = 1) -> Bool {
    print (#function)
    return x != 0
}

func bar (_ x:Int = 1) -> Bool {
    print (#function)
    return x != 0
}

func jibber (_ x:Int = 1) -> Bool {
    print (#function)
    return x != 0
}

Are the following if statements equivalent?

// One
if foo () && bar () {
    print ("foo && bar...")
}
else if jibber () {
    print ("jibber...")
}
else {
    print ("...")
}

// Two
if foo () && bar (), jibber () {
    print ("foo & bar & jibber...")
}
else {
    print ("...")
}

To find out, play with different combinations of arguments: 0s and 1s.

Copying the book’s code and adding this clause, string.rangeOfCharacter(from: letters) != nil, as a comma delimited argument (if _, _, _) this clause would be evaluated as an and statement. Essentially saying (if there is a decimal and a letter). This can never be true and so a return statement will never be issued. What we want to say is (if there is a decimal or a letter). In code this would look like this:

if (this || that) { }

Now, being the case that existingTextHasDecimalSeparator and replacementTextHasDecimalSeparator both check for decimals and both clauses are dependent these clauses must be grouped in an **and** statement. Also, being the case that string.rangeOfCharacter checks for letters this can be in added as an **or** statement. These clauses can all live on a single line as:

if ((existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil) || string.rangeOfCharacter(from: letters) != nil)

Notice the grouping by parenthesis. Your code with the extra else-if block accomplishes the same thing and works for this example. But in other examples it would work the same because if blocks don’t fallthrough.

This is very subtle and confusing. I recommend opening a playground and trying different combinations. Here are the ones that helped me:

if ((true && true) || false) {
   print("test1")
}

if (true && false) {
    print("test2")
}

if (true || false) {
    print("test3")
}