Search code examples
swiftloopsenums

Why on enums is .allCases.forEach not a loop (continue and break don't work)?


I want to iterate through all values in an enum but given certain conditions I want to give up and move to the next value. Any attempt at a break or continue statement and the compiler says that '.allCases.forEach' is not a loop as in 'func test1'.

The best workaround I've come up with is func test2 but it seems "non-swifty" and I could just have a bunch of nested if blocks that seem extra. So what is .allCases.forEach if not a loop and more importantly, is there a different/better way to iterate through the enum?

Updated: added func 'processFoolPower' with solution 1 as a small example of the goal. Think large chess board.

enum directions: Int, CaseIterable {
    case up = 0, upRight, right, downRight, down, downLeft, left, upLeft, invalid
}

func test1() {
    print("forEach: ")
    moveDirections.allCases.forEach {
        if $0 == .up { () }         // I want to { continue } here to next direction
        if $0.rawValue == 4 { () }  // or { continue } here to next direction
        testPrint(test: $0)
    }
}

func test2() {
    print("for : ")
    for direction in 0..<moveDirections.allCases.count {
        if moveDirections(rawValue: direction) == .up { continue }
        if direction == 4 { continue }
        testPrint(test: moveDirections(rawValue: direction).self!)
    }
}

func testPrint(test: moveDirections) {
    print(test.self)
}


    mutating func processFoolPower(foolPiece: gamePiece, owner: Bool) {

        var testPiece: gamePiece = MockData.blankPiece
        
        for direction in moveDirections.allCases {
            let testSquare = foolPiece.location! + SquareShift(direction)
            if testSquare < 0 || testSquare > 143  { continue }                                     // test for off top/bottom of board
            switch direction {
            case .down, .up, .left, .right:
                if isStraight(from: foolPiece.location!, to: testSquare) == .invalid { continue }   // test for off sides of board
            case .upLeft, .downLeft, .upRight, .downRight: ()
                if isDiagonal(from: foolPiece.location!, to: testSquare) == .invalid { continue }   // test for off sides of board
            case .invalid: ()
            }
            testPiece = pieces.first(where: { $0.location == testSquare }) ?? MockData.blankPiece
            if testPiece.status != .dead { executeKill(pieceId: testPiece.id, explanation: .blownUp) }
        }
        executeKill(pieceId: foolPiece.id, explanation: .exploded)
    }

Solution

  • forEach is an array function that iterates over the elements, invoking a closure, like map; It isn't a Swift loop construct, so you can't use break or continue.

    Your second attempt is closer to what you need, but unnecessarily complicated. There is no need for an index or raw values.

    You can use a for in loop with a collection's elements directly:

    enum directions: Int, CaseIterable {
        case up = 0, upRight, right, downRight, down, downLeft, left, upLeft, invalid
    }
    
    for direction in directions.allCases { 
        if direction == .up { 
            continue 
        }
        print(direction)
    }