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 toEquatable
,Failure
conforms toEquatable
, andFailure
conforms toError
.
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
Drawing at Cocoa with Swift Creates an Error
[Bool]' to 'Nil' Always Returns True - Issue in Swiftui
Aws Cognito Credentialsprovider.Login Always Shows Nil (Swift)
Force Refresh on Another Viewcontroller Component with Swift
Using Cfarraygetvalueatindex in Swift with Unsafepointer (Aupreset)
Adding Items to The Dock Menu from My View Controller in My Cocoa App
How to Loop Over The Output of a Publisher with Combine
Realitykit - Add Force to Entity at Specific Point
Error When Running Coreml in The Background: Error Computing Nn Outputs Error
Urlsession Datatask Method Returns 0 Bytes of Data
Alamofire Request Not Working in Swift 4 Project
Swift Initialization Rule Confusion
How to Convert an Array to List in Realm
Show Status Bar Only for iPhone X