Combine an IF LET with an OR in Swift
Asked Answered
U

4

6

Is there a graceful way to combine two if let statements by an or operator. For instance, I need to check for the strings "pass", "true", or the integer 1. The following function, does just that...

func test(content: Any) -> String {
    if let stringValue = (content as? String)?.lowercased(),
        ["pass", "true"].contains(stringValue) {
        return "You Passed"
    }
    if let numValue = (content as? Int),
        1 == numValue {
        return "YOU PASSED"
    }
    return "You Failed"
}

test(content: "Pass") //"You Passed"
test(content: 1) //"YOU PASSED"

What is the most simple way to combine these two if let statements to handle the data being passed in?

Ultrared answered 24/5, 2018 at 14:55 Comment(3)
The second if let could be added to the first as an else if case, as the two are mutually exclusive.Forbidden
Ideally, you should make the function return only Bool and than have a second function translate that to a String but having 3 values, even with distinct types to mean the same, seems like a design problem on its own. Try to avoid Any.Sterilize
"YOU PASSED" - is different than "You Passed", so it seems there's no need for the OR operator.Messina
T
3

You can't do one if let with an or inside because if it gets inside the statement because of one condition, the other can be nil, and with the if let you are assuring that the var is not nil inside the statement.

I would just check if they are not nil and do something.

Even in this example you do not need to use if lets. (I use one to show how to use with an AND operator, with OR it is not possible.

if let stringValue = (content as? String)?.lowercased(), (["pass", "true"].contains(stringValue) || 1 == (numValue ?? 0)) {
        return "You Passed"
    }

Also, if you just want to check if the value of a variable is nil or not, it is better to use != nil instead of if let, according to SwiftLint.

Thomasthomasa answered 24/5, 2018 at 15:7 Comment(0)
S
0

You can probably just inline everything:

func test(content: Any) -> String {
    if ["pass", "true"].contains((content as? String)?.lowercased())
        || (content as? Int) == 1 {
        return "You Passed"
    }
    return "You Failed"
}

but in general, I would advise to split such things into multiple methods, one to check the value and second to translate it to a String:

func test(content: Any) -> Bool {
   return ["pass", "true"].contains((content as? String)?.lowercased())
       || (content as? Int) == 1
}

func testToString(content: Any) -> String {
   return test(content: content) ? "You passed" : "You failed"
}

You could also convert all values to a String first and check that:

func test(content: Any) -> Bool {
    let stringValue = String(describing: content).lowercased()

    return ["1", "pass", "true"].contains(stringValue)
}

However, that will add some false positives because it will return true even for "1" string and I would generally advise against that.

Sterilize answered 24/5, 2018 at 15:18 Comment(0)
B
0

Had a similar problem and wanted to combine two let try? assignments to keep by code DRY. Got it working, but needed to ensure to get the parentheses right:

if let number = try? ((try? Number.parse("1")) ?? Number.parse("2")) {
    print("Yay, a number: \(number)")
} else {
    print("Nope, not a number")
}

// => Prints "Yay, a number: 1"
if let number = try? ((try? Number.parse("a")) ?? Number.parse("2")) {
    print("Yay, a number: \(number)")
} else {
    print("Nope, not a number")
}

// => Prints "Yay, a number: 2"

I mocked the following for this example, in the real code I parsed a number using PhoneNumberKit.

struct Number {
    static func parse(_ numberString: String) throws -> Int {
        if let result = Int(numberString) {
            return result
        } else {
            throw ParsingError.notANumber
        }
    }

    enum ParsingError: Error {
        case notANumber
    }
}
Baumgardner answered 6/9, 2019 at 7:7 Comment(0)
N
-1

Clearly I was distracted when I answered this question and didn't read it closely enough. No, you write an expression with 2 optional binding if let statements where only one of them will be true.

you can't say

if let x = y let a = b

it seems the cleanest way to do this is with an else:

if let x = y { code }
else if let a = b { other code } 

Previous answer that did not answer the question that was asked:

Yes, you can combine multiple if let statements:

var x: Int? = 1
var y: String? = ""
var z = 2

if let x = x,
  let y = y,
  z == 2 {
    //do stuff
}

The if block above will only be executed if x and y are not nil (and z == 2)

Even better, each subsequent if let optional binding can use an optional you unwrapped with the previous if let.

Numbles answered 24/5, 2018 at 15:0 Comment(1)
You are using AND operator not Or, it is not possible to combine if lets with an Or operator.Thomasthomasa

© 2022 - 2024 — McMap. All rights reserved.