2

I have an enum where each case has different (or none) associated values. The enum is not equatable.

I'm using on several places if case to check if a value is of a specific case.

enum MyEnum {
    case justACase
    case numberCase(Int)
    case stringCase(String)
    case objectCase(NonEquatableObject)
}

let myArray: [MyEnum] = [.justACase, .numberCase(100), .stringCase("Hello Enum"), .justACase]

if case .justACase = myArray[0] {
    print("myArray[0] is .justACase")
}

if case .numberCase = myArray[1] {
    print("myArray[1] is .numberCase")
}

But I would like to extract that into its own method where I pass on the case I want to check for. I imagine something like the following. (Please write in the comments if something is not clear.)

func checkCase(lhsCase: /*???*/, rhsCase: MyEnum) -> Bool {
    if case lhsCase = rhsCase {
        return true
    } else {
        return false
    }
}

// Usage:
if checkCase(lhsCase: .justACase, rhsCase: myArray[0]) {
    print("myArray[0] is .justACase")
}

In my example above lhsCase should not be of type MyEnum I assume, as this would mean I'd try to compare two properties (See code just below) and would require my enums to be equatables. Which they are not.

I'm not sure if this is even possible that way.

If the following would work it would also solve my problem but it does not.

if case myArray[3] = myArray[0] {
    print("myArray[0] is .justACase")
}
Marco Boerner
  • 1,243
  • 1
  • 11
  • 34

2 Answers2

3

The answer of Shadowrun is a good start. Through the CasePaths library I found EnumKit, which was partially the inspiration for CasePaths. However it gives much simpler methods to compare cases. See my implementation below.

Using this library comes with some nice bonuses, it allows enums that have associated values to be made equatable by just comparing the cases and ignoring the values. This might not always be desired but come in quite handy in a lot of cases. I use it to compare ReSwift Actions with Associated values in my tests.

import Foundation
import EnumKit

class NonEquatableObject {
}

enum MyEnum: CaseAccessible { // <-- conform to CaseAccessible
    case justACase
    case numberCase(Int)
    case stringCase(String)
    case objectCase(NonEquatableObject)
}

let myArray: [MyEnum] = [.justACase, .numberCase(100), .stringCase("Hello Enum"), .justACase]

if case .justACase = myArray[0] {
    print("myArray[0] is .justACase")
}

if case .numberCase = myArray[1] {
    print("myArray[1] is .numberCase")
}


func checkCase(lhsCase: MyEnum, rhsCase: MyEnum) -> Bool {
    if case lhsCase = rhsCase {
        return true
    } else {
        return false
    }
}

// Usage:
if checkCase(lhsCase: .justACase, rhsCase: myArray[0]) { //<-- allows to pass variables or just the cases (unfortunately not without associated value.)
    print("myArray[0] is .justACase")
}

if case myArray[3] = myArray[0] { //<-- allows this here too
    print("myArray[0] is .justACase")
}

// Bonus: Adding equatable if associated values are not equatable. Looking at the case only.
extension MyEnum: Equatable {
    static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
        lhs.matches(case: rhs)
    }
}

if myArray[3] == myArray[0] {
    print("myArray[3] == myArray[0]")
}
Marco Boerner
  • 1,243
  • 1
  • 11
  • 34
2

There's a library CasePaths that might do what you want: something like this:

struct NonEO {
    var a: Any
}

enum MyEnum {
    case justACase
    case numberCase(Int)
    case stringCase(String)
    case nonEO(NonEO)
}

let myArray: [MyEnum] = [.justACase, .nonEO(NonEO(a: 42)), .stringCase("Hello Enum"), .justACase, .nonEO(NonEO(a: "Thing"))]

func sameCase<T>(casePath: CasePath<MyEnum, T>,
                 lhs: MyEnum, rhs: MyEnum) -> Bool {
    (casePath.extract(from: lhs) != nil)
        && casePath.extract(from: rhs) != nil
}

sameCase(casePath: /MyEnum.justACase, lhs: myArray[0], rhs: myArray[1]) // FALSE
sameCase(casePath: /MyEnum.justACase, lhs: myArray[0], rhs: myArray[3]) // TRUE

sameCase(casePath: /MyEnum.nonEO, lhs: myArray[1], rhs: myArray[4]) // TRUE
sameCase(casePath: /MyEnum.nonEO(.init(a: 42)), lhs: myArray[1], rhs: myArray[4]) // FALSE
Shadowrun
  • 3,572
  • 1
  • 15
  • 13
  • That's an interesting solution, while looking through their project on github I found a link to EnumKit (see my answer) which is pretty much exactly what I was looking for. Thank you for your answer! It was of great help! : ) – Marco Boerner Apr 20 '21 at 12:17