How to pass an enum case into a function that uses 'if case' to check for a property's case

1.4k views Asked by At

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")
}
2

There are 2 answers

0
Marco Boerner On BEST ANSWER

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]")
}
1
Shadowrun On

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