How case works in if-case

Range Containment

Use the ~= operator, as @Alexander mentioned.

 if (1...10).contains(n) {
     print("pass")
 }

 switch n {
     case 1...10: print("pass")
     default: print("bug")
 }

 if case 1...10 = n {
     print("pass")
 }

 if 1...10 ~= n {
     print("pass")
 }

You can even do Ranges of Characters:

 if "a"..."z" ~= "a" {
     print("pass")
 }

Overloading ~=

I made a couple overloads, so that you can not only check for Range, but also Arrays and Sets

func ~=<T: Equatable> (pattern: [T], value: T) -> Bool { return pattern.contains(value) }
func ~=<T: Equatable> (pattern: Set<T>, value: T) -> Bool { return pattern.contains(value) }
func ~=<T: Equatable> (pattern: Set<T>, value: Set<T>) -> Bool { return pattern.union(value).count < pattern.count + value.count }

Which allows you to do this:

if [1, 2, 3] ~= 2 {
    print("pass")
}

if [1, 2, 3] ~= [3, 4, 5] {
    print("pass")
}

Tuples

However, when using tuples, you'll still have to use if case

 if case (1...10, 1...10) = (number1, number1) {
     print("0")
 }

You can even do the _ syntactic sugar

 if case (1...10, _) = (number1, number1) {
     print("0")
 }

Optionals

 if case .some = Optional(number) {
     print("pass")
 }

 if case .some(let x) = Optional(number) {
     print(x)
 }

 if case let x? = Optional(number) {
     print(x)
 }

Just another note about these Swift operators. One must remember that Swift has a very advanced and abstracted compiler, so keywords are just keywords, and their behavior depends on their usage. Unlike C (and other syntactically related languages from C++ to JavaScript), where if is simply used to test a Boolean value (or something that can be converted to one), in Swift, the concept of the if statement is much more broad. I generally think of it gating access to a scope by using a variety of techniques, including Boolean tests.

You can think of if, guard, while, and repeat-while as more general control flow statements, with much more advanced behavior. Certainly they can still test a Boolean value, but they can also test other conditions as well. In your scenario, the condition being tested is whether some variable matches a defined pattern (does age match the pattern 20...30).

You can also test whether a variable was successfully set to a non-nil value (if let). The result of the let operation doesn't ever return a Boolean, but since it occurs within the if statement, the Swift runtime knows that it's part of the control flow. Also note that this slightly changes the behavior of let, insofar as any code inside the if block now sees the new non-nil value and is assured it's not nil.

These behaviors can also be combined with commas, like this:

if !skipAgeTest,                 // `if` tests a Boolean
    let age = Double(ageString), // `if` tests optional assignment
    case 20...30 = age           // `if` tests pattern matching
    {
    // `age` variable exists and is not `nil` and is between `20` and `30`, inclusive
}

so the concept of if let or if case being separate operators is... not exactly thinking about it in the right way.

And like I said, this syntax is also valid in other control flows:

while !skipAgeTest,
    let age = Double(ageString),
    case 20...30 = age {
    // `age` is validated
    // Probably also change `ageString` while we're in here
}


guard !skipAgeTest,
    let age = Double(ageString),
    case 20...30 = age
    else {
    // `age` is invalid, and not available in this block
}
// `age` is valid

@matt does a good job of explaining what that code does. I'm here to suggest a better alternative.

You can use the ~= operator to check ranges. It's a regular operator/function that just returns a Bool, with no special language magic.

if 20...30 ~= age {
   print ("in range.")
}

The operator is if case, so you can't put parentheses. The syntax and behavior are based on those of the case statement in a Swift switch statement (see my online book if you need details). In a case statement, 20...30 is an interval, used as a pattern, which operates by using contains against the interval. The equals sign is indeed truly confusing, but that was their first attempt at a syntax for expressing what the case statement should be comparing with (i.e. the tag that comes after the switch keyword in a switch statement).

So, if you understand this:

switch age {
case 20...30:
    // do stuff
default:break
}

... then you understand how it is morphed directly into this:

if case 20...30 = age {
   // do stuff
}

Tags:

Swift