Writing an Enum Case Check to a Bool Variable Without Equatable Conformance

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

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

Can I use case enum comparison as a boolean expression?

Unfortunately, you cannot (directly) use a case condition as a Bool expression. They are only accessible in statements such as if, guard, while, for etc.

This is detailed in the language grammar, with:

case-condition → case ­pattern­ initializer­

This is then used in a condition:

condition → expression |­ availability-condition |­ case-condition­ | optional-binding-condition

(where expression represents a Bool expression)

This is then used in condition-list:

condition-list → condition­ | condition ­, ­condition-list
­

which is then used in statements such as if:

if-statement → if­ condition-list­ code-block­ else-clause­ opt­

So you can see that unfortunately case-condition is not an expression, rather just a special condition you can use in given statements.

To pack it into an expression, you'll either have to use an immediately-evaluated closure:

return { if case .active = session.state { return true }; return false }()

Or otherwise write convenience computed properties on the enum in order to get a Bool in order to check for a given case, as shown in this Q&A.

Both of which are quite unsatisfactory. This has been filed as an improvement request, but nothing has come of it yet (at the time of posting). Hopefully it's something that will be possible in a future version of the language.

Swift enum conformance to Equatable when result type used as associated value: Type doesn't conform to protocol Equatable

Enum can automatically conform to Equatable if its associated values conform to Equatable, from docs:

For an enum, all its associated values must conform to Equatable. (An enum without associated values has Equatable conformance even without the declaration.)

And the Result<Success, Failure> only conforms to Equatable when

Success conforms to Equatable, Failure conforms to Equatable, and Failure conforms to Error.

Your result's failure is only conform to Error and Error is not Equatable yet. You can try to replace your Error with a type that conforms to both Error and Equatable

enum BookAction: Equatable {
case dataResponse(Result<Book, ActionError>)
}

struct ActionError: Error, Equatable { }

Ref:

https://developer.apple.com/documentation/swift/equatable
https://developer.apple.com/documentation/swift/result

How do I know the type of an enum with associated value in a generic way?

You just need to make your enumeration conform to Equatable and compare the associated values for equality:

enum MyEnum: Equatable {
case one
case two
case three(user: String)
}


var array: [MyEnum] = []
func add(_ value: MyEnum) {
if array.contains(value) { return }
array.append(value)
}


add(.one)
add(.one)
array
add(.three(user: "a"))
add(.three(user: "b"))
add(.three(user: "a"))

array[0] // one
array[1] // three(user: "a")
array[2] // three(user: "b")

To check if the last element is of case .three:

if case .three = array.last {
print(true)
}

How can I use a Swift enum as a Dictionary key? (Conforming to Equatable)

Info on Enumerations as dictionary keys:

From the Swift book:

Enumeration member values without associated values (as described in
Enumerations) are also hashable by default.

However, your Enumeration does have a member value with an associated value, so Hashable conformance has to be added manually by you.

Solution

The problem with your implementation, is that operator declarations in Swift must be at a global scope.

Just move:

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
return lhs.toInt() == rhs.toInt()
}

outside the enum definition and it will work.

Check the docs for more on that.



Related Topics



Leave a reply



Submit