39

I'm learning Swift and tried to program the game "Bullseye" from Ryan Wenderlich by my own before watching the videos.

I needed to give the user points depending on how close to the target number he was. I tried to calculate the difference and than check the range and give the user the points, This is what I did with If-else (Couldn't do it with switch case):

private func calculateUserScore() -> Int {
    let diff = abs(randomNumber - Int(bullsEyeSlider.value))
    if diff == 0 {
        return PointsAward.bullseye.rawValue
    } else if diff < 10 {
        return PointsAward.almostBullseye.rawValue
    } else if diff < 30 {
        return PointsAward.close.rawValue
    }
    return 0 // User is not getting points. 
}

Is there a way to do it more elegantly or with Switch-Case? I couldn't just do diff == 0 for example in the case in switch case as xCode give me an error message.

Ankit Jayaswal
  • 5,549
  • 3
  • 16
  • 36
John Doah
  • 1,839
  • 7
  • 25
  • 46
  • Possible duplicate of [Lesser than or greater than in Swift switch statement](https://stackoverflow.com/questions/31656642/lesser-than-or-greater-than-in-swift-switch-statement) – Ali Moazenzadeh Oct 05 '18 at 12:38
  • 2
    `case 0: return PointsAward.bullseye.rawValue` and `case 1..<10: return PointsAward.almostBullseye.rawValue`, etc... – holex Oct 05 '18 at 12:41
  • It's hard to see the point of using an enum PointsAward since you are only interested in the rawValue. Do you use the enum elsewhere in your code? – Mike Taverne Oct 05 '18 at 14:55
  • 1
    No, I'm new to Swift and just wanted to try using basic enums. Yeah, it's not really needed I agree with you. – John Doah Oct 05 '18 at 14:57
  • Dude its so funny, I also googled this while doing the same bullseye app on the RW. Seems like RW is everywhere! – Hasaan Ali Sep 21 '19 at 16:50

6 Answers6

75

This should work.

private func calculateUserScore() -> Int {
    let diff = abs(randomNumber - Int(bullsEyeSlider.value))
    switch diff {
    case 0:
        return PointsAward.bullseye.rawValue
    case 1..<10:
        return PointsAward.almostBullseye.rawValue
    case 10..<30:
        return PointsAward.close.rawValue
    default:
        return 0
    }
}

It's there in the The Swift Programming Language book under Control Flow -> Interval Matching.

Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50
14

In case that you need to check if a value is bigger than any number or between 2 values use it is possible to use where instead of if looks a bit cleaner this will work

func isOutdated(days: Int) -> Outdated {

    var outdatedStatus = Outdated.none

    switch days {
    case _ where days < 5:
        outdatedStatus = .tooLow
    case 5...10:
        outdatedStatus = .low
    case 11...20:
        outdatedStatus = .high
    case _ where days > 20:
        outdatedStatus = .expired
    default:
        outdatedStatus = .none
    }
    return outdatedStatus
}
PDK
  • 1,476
  • 1
  • 14
  • 25
7

You can return the values you want accordingly:

switch diff {
case 0:
    print("Bull Eye")
case 1..<10:
    print("Almost Bull Eye")
case 10..<30:
    print("Close")
default:
    print("Too Far")
}
aheze
  • 24,434
  • 8
  • 68
  • 125
iOSer
  • 2,241
  • 1
  • 18
  • 26
6

Only for you:

enum PointsAward: Int {
    case close
    case almostBullseye
    case bullseye
}

private func calculateUserStory() -> Int {
    let bullsEyeSliderValue = 9
    let randomNumber = 100
    let diff = abs(randomNumber - Int(bullsEyeSliderValue))
    switch diff {
    case 0:
        return PointsAward.bullseye.rawValue
    case 0..<10:
        return PointsAward.almostBullseye.rawValue
    case 0..<30:
        return PointsAward.close.rawValue
    default: return 0
    }
}
Vitaliy Rusinov
  • 256
  • 1
  • 10
4

You have to use range-operators as case in switch-statement:

a...b // It have range from a to b, means b is included as well

a..<b // It have range from a to b-1, means b is not included

private func calculateUserScore() -> Int {
    let diff = abs(randomNumber - Int(bullsEyeSlider.value))
    switch diff {
    case 0: return PointsAward.bullseye.rawValue
    case 1..<10: return PointsAward.almostBullseye.rawValue
    case 10..<30: return PointsAward.close.rawValue
    default: return 0
    }
}
Community
  • 1
  • 1
Ankit Jayaswal
  • 5,549
  • 3
  • 16
  • 36
1

In my case the compiler took CGFloat as a closed range of double, so I had to explicitly tell compiler that I am checking CGFloat range.

   var progress: CGFloat!
   switch CGFloat(progress) {
    case 0 ... 0.25:
        barColor = .red
    case 0.25 ... 0.5:
        barColor = .yellow
    default:
        break
    }