Reputation: 2809
I have an Enum with associated values like this one:
enum SomeState {
case loggedOut(redirectAfterLogin: Module? = nil)
case loggedIn
}
Now in some cases I want to compare two states exactly (like are both logged out and also is the redirect target equal) and sometimes I just want to know if they are both logged out. My question regards the second case: I want to check if the state is logged out and write that to a variable, but obviously I can't implement Equatable
to just account for the general case ignoring the parameters.
One way of achieving this would be to implement a computed property isLoggedOut
on the Enum itself, but since this here is just an example and my actual code is much larger, this is not an option for me.
A second way (the one I currently use) is this:
func whatever() {
if case .loggedOut = self.state {
self.isLoggedOut = true
} else {
self.isLoggedOut = false
}
}
This works, but I would rather write something like this:
func whatever() {
self.isLoggedOut = (case .loggedOut = self.state)
}
Am I missing something or is it really not possible to write the case comparison of the if
-clause to a variable directly (or some similar one-line-solution)?
Upvotes: 1
Views: 1717
Reputation:
I don't think it's possible to avoid using SomeState
before .loggedOut
. But even with that, this is way better than the option that's built into the language, which is in the other answers.
It's better if you can make Module
and SomeState
be Equatable
, but not necessary.
public extension Mirror {
/// Get the associated value from an `enum` instance.
func getAssociatedValue<AssociatedValue>(
_: AssociatedValue.Type = AssociatedValue.self
) -> AssociatedValue? {
guard let childValue = children.first?.value
else { return nil }
if let associatedValue = childValue as? AssociatedValue {
return associatedValue
}
let labeledAssociatedValue = Mirror(reflecting: childValue).children.first
return labeledAssociatedValue?.value as? AssociatedValue
}
}
/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter makeCase: Looks like `Enum.case`.
public func ~= <Enum: Equatable, AssociatedValue>(
makeCase: (AssociatedValue) -> Enum,
instance: Enum
) -> Bool {
Mirror(reflecting: instance).getAssociatedValue().map(makeCase)
== instance
}
/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter makeCase: Looks like `Enum.case`.
public func ~= <Enum, AssociatedValue>(
makeCase: (AssociatedValue) -> Enum,
instance: Enum
) -> Bool {
let instanceMirror = Mirror(reflecting: instance)
guard let dummyCase = instanceMirror.getAssociatedValue().map(makeCase)
else { return false }
return
Mirror(reflecting: dummyCase).children.first?.label
== instanceMirror.children.first?.label
}
struct ๐งจ {
enum SomeState {
struct Module { }
case loggedOut(redirectAfterLogin: Module? = nil)
case loggedIn
}
var isLoggedOut: Bool
var state: SomeState
mutating func whatever() {
isLoggedOut = SomeState.loggedOut ~= state
}
}
More example usage:
enum ๐ง: Equatable {
case tuple(cat: String, hat: String)
case labeled(cake: String)
case noAssociatedValue
}
let tupleCase = ๐ง.tuple(cat: "๐ฏ", hat: "๐งข")
XCTAssertTrue(๐ง.tuple ~= tupleCase)
XCTAssertTrue( ๐ง.labeled ~= ๐ง.labeled(cake: "๐ฐ") )
let makeTupleCase = ๐ง.tuple
XCTAssertFalse(makeTupleCase ~= ๐ง.noAssociatedValue)
switch tupleCase {
case ๐ง.labeled: XCTFail()
case makeTupleCase: break
default: XCTFail()
}
Upvotes: 0
Reputation: 54706
You simply need to change whatever
to be a computed property rather than a function, modify the assignments to isLoggedOut
to be return
statements and you've got your isLoggedOut
property.
var isLoggedOut: Bool {
if case .loggedOut = self.state {
return true
} else {
return false
}
}
Simplified example code where the type holding the state
property defines the isLoggedOut
property as well:
enum SomeState {
case loggedOut
case loggedIn
}
struct User {
var state: SomeState
var isLoggedOut: Bool {
if case .loggedOut = self.state {
return true
} else {
return false
}
}
}
Upvotes: 2
Reputation: 131398
How about this:
enum SomeState {
case loggedOut(redirectAfterLogin: Int? = nil)
case loggedIn
var isLoggedOut: Bool {
switch self {
case .loggedOut(_): return true
default: return false
}
}
}
var aState: SomeState = .loggedOut()
if aState.isLoggedOut {
print("logged out")
} else {
print("not logged out")
}
(I switched the associated value redirectAfterLogin to a simple scalar Int so it would compile for those of us who don't have the definition of your Module type. you'd need to switch it back.)
The trick here is the computed property isLoggedOut, which uses a switch statement that ignores the associated value for the loggedOut case.
Upvotes: 1