Rock Paper Scissors in Swift I'm going through the eBook Intro to App Development with Swift by Apple and I am making the Rock, Paper, Scissors app in lesson 20.
This is the assignment that I got:

[...] Name the enum Sign. Add a calculated property to give the emoji that represents the Sign.
[...]
You need to be able to compare two Sign instances to give a GameState. ​
For example, a player’s .rock and the app’s .paper would give you .lose.
Add an instance method to Sign that takes another Sign, representing the opponent’s turn, as a parameter. The method should return a GameState based on a comparison between self and the opponent’s turn. [...]

The code that I wrote works but I feel like this can be done a lot cleaner. Maybe it's Xcode's indentation that looks a bit weird to me, maybe there's a better solution?
I'll start with GameState.swift (these 4 gamestates were given in the instructions):
import Foundation

enum GameState {
    case start, win, lose, draw
}

And here's Sign.swift:
import Foundation

enum Sign {
    case rock, paper, scissors
    
    var emoji: String {
        switch self {
        case .rock: return &quot;&quot;
        case .paper: return &quot;✋&quot;
        case .scissors: return &quot;✌️&quot;
        }
    }
    
    func beats(otherSign: Sign) -&gt; GameState {
        switch self {
        case .rock: do {
            if otherSign == .paper {
                return .lose
            } else if otherSign == .scissors {
                return .win
            }
            return .draw
            }
        case .scissors: do {
            if otherSign == .rock {
                return .lose
            } else if otherSign == .paper {
                return .win
            }
            return .draw
            }
        case .paper: do {
            if otherSign == .rock {
                return .win
            } else if otherSign == .scissors {
                return .lose
            }
            return .draw
            }
        }
    }
}
First, the do { ... } blocks inside the cases are not needed:
func beats(otherSign: Sign) -&gt; GameState {
    switch self {
    case .rock:
        if otherSign == .paper {
            return .lose
        } else if otherSign == .scissors {
            return .win
        }
        return .draw
    case .scissors:

    // ...
}

But why use a switch-statement for self and an if-statement for otherSign? The    if-statement is error-prone (we might e.g. forget a case), whereas the switch-statement perfectly matches the enum declaration and the compiler checks the exhaustiveness, i.e. that all cases are treated exactly once:
func beats(otherSign: Sign) -&gt; GameState {
    switch self {
    case .rock:
        switch otherSign {
        case .paper:    return .lose
        case .scissors: return .win
        case .rock:     return .draw
        }
    case .scissors:

    // ...
}

This is already more compact than the original code, but still clear and easy to read.
Now we go one step further: A single switch-statement is sufficient because you can switch on a tuple in Swift:
func beats(otherSign: Sign) -&gt; GameState {
    switch (self, otherSign) {
    case (.rock, .rock):     return .draw
    case (.rock, .paper):    return .lose
    case (.rock, .scissors): return .win
        
    case (.paper, .rock):     return .win
    case (.paper, .paper):    return .draw
    case (.paper, .scissors): return .lose
        
    case (.scissors, .rock):     return .lose
    case (.scissors, .paper):    return .win
    case (.scissors, .scissors): return .draw
    }
}

It is clear to the reader that (and how) all possible combinations are handled. Repeated or missing cases are detected by the compiler.
There is also another, completely different option. We can assign the integer values 0, 1, 2 to rock, paper, scissors, respectively, e.g. by making it an integer based enumeration:
enum Sign: Int {
    case rock, paper, scissors

    // ...
}

then the outcome of the comparison can efficiently be computed from the difference of the integer values:
func beats(otherSign: Sign) -&gt; GameState {
    let diff = self.rawValue - otherSign.rawValue

    // ...
}

I'll leave it to you to work out the details (hint: modulo arithmetic), and to decide which variant your prefer: A (verbose) switch-statement with 9 cases, or a compact mathematical calculation.