How to do an if-else comparison on enums with arguments [duplicate] - swift

This question already has answers here:
How to compare enum with associated values by ignoring its associated value in Swift?
(9 answers)
Compiler error when comparing values of enum type with associated values?
(2 answers)
Closed 5 years ago.
Language: Swift2.3
For example let's I'll show you different kinds of enums
enum Normal {
case one
case two, three
}
enum NormalRaw: Int {
case one
case two, three
}
enum NormalArg {
case one(Int)
case two, three
}
Switch can be used on all three enums like so:
var normal: Normal = .one
var normalRaw: NormalRaw = .one
var normalArg: NormalArg = .one(1)
switch normal {
case .one: print("1")
default: break
}
switch normalRaw {
case .one: print(normalRaw.rawValue)
default: break
}
switch normalArg {
case .one(let value): print(value)
default: break
}
On the if-else statement though I can only do comparison for Normal and NormalRaw, and an error message shows for NormalArg, so I can't run the code
Binary Operator '==' cannot be applied to operands of type NormalArg
and _
Here's the code example:
if normal == .two { // no issue
.. do something
}
if normalRaw == .two { // no issue
.. do something
}
if normalArg == .two { // error here (the above message)
.. do something
}
if normalArg == .one(_) { // error here (the above message)
.. do something
}
if normalArg == .three { // error here (the above message)
.. do something
}
Any Ideas? I'm not really doing anything with this code, I'm just wondering as to why we can't do comparison.

The trick is to not actually check with == but rather use the case keyword in conjunction with a single = in your if statement. This is a little counter intuitive in the beginning but just like if let, you get used to it pretty fast:
enum Normal {
case one
case two, three
}
enum NormalRaw: Int {
case one = 1
case two, three
}
enum NormalArg {
case one(Int)
case two, three
}
let normalOne = Normal.one
let normalRawOne = NormalRaw.one
let normalArgOne = NormalArg.one(1)
if case .one = normalOne {
print("A normal one") //prints "A normal one"
}
if case .one = normalRawOne {
print("A normal \(normalRawOne.rawValue)") //prints "A normal 1"
}
if case .one(let value) = normalArgOne {
print("A normal \(value)") //prints "A normal 1"
}
The point is that in Swift you only get equation of enums for free if your enum uses a raw type or if you have no associated values (try it out, you can't have both at the same time). Swift however does not know how to compare cases with associated values - I mean how could it? Let's look at this example:
Normal.one == .one //true
Normal.one == .two //false
NormalRaw.one == .one //true
NormalRaw.one == .two //false
NormalArg.one(1) == .one(1) //Well...?
NormalArg.one(2) == .one(1) //Well...?
NormalArg.one(1) == .two //Well...?
Maybe this makes it clearer why this cannot work out of the box:
class Special {
var name: String?
var special: Special?
}
enum SpecialEnum {
case one(Special)
case two
}
var special1 = Special()
special1.name = "Hello"
var special2 = Special()
special2.name = "World"
special2.special = special1
SpecialEnum.one(special1) == SpecialEnum.one(special2) //Well...?
So if you want enums with associated values, you'll have to implement Equatable protocol in your enum by yourself:
enum NormalArg: Equatable {
case one(Int)
case two
static func ==(lhs: NormalArg, rhs: NormalArg) -> Bool {
switch (lhs, rhs) {
case (let .one(a1), let .one(a2)):
return a1 == a2
case (.two,.two):
return true
default:
return false
}
}
}

The answer is Equatable Protocol.
Now let's see how it works.
Consider this enum for example:
enum Barcode {
case upca(Int, Int)
case qrCode(String)
case none
}
If we check the equatable operator == on the enum it will fail.
// Error: binary operator '==' cannot be applied to two Barcode operands
Barcode.qrCode("code") == Barcode.qrCode("code")
How to fix this using Equatable Protocol?
extension Barcode: Equatable {
}
func ==(lhs: Barcode, rhs: Barcode) -> Bool {
switch (lhs, rhs) {
case (let .upca(codeA1, codeB1), let .upca(codeA2, codeB2)):
return codeA1 == codeA2 && codeB1 == codeB2
case (let .qrCode(code1), let .qrCode(code2)):
return code1 == code2
case (.None, .None):
return true
default:
return false
}
}
Barcode.qrCode("code") == Barcode.qrCode("code") // true
Barcode.upca(1234, 1234) == Barcode.upca(4567, 7890) // false
Barcode.none == Barcode.none // true

Related

Accessing Associated Values in an Array of Swift Enum in an Opaque Fashion

I haven't really found what I need in the associated questions. That may be because I'm a bit thick, and didn't see it, but here's my quandary:
I have a computed property that returns an Array of enums with associated values.
The values are all the same types (in my use case, the type is a "flag"), but the enums are different.
I'd like to see if there was some way to cycle through the Array, looking at only the associated values, regardless of the enum type. I am attaching a small playground that shows what I mean:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
print(String(describing: each))
}
print();
// This works:
for each in a {
if case let .valueA(value) = each {
print(value)
}
if case let .valueB(value) = each {
print(value)
}
if case let .valueC(value) = each {
print(value)
}
}
print();
// So does this:
for each in a {
var value: Int = 0
switch each {
case .valueA(let val):
value = val
case .valueB(let val):
value = val
case .valueC(let val):
value = val
}
print(value)
}
// What I want:
//for each in a {
// if case let A(value) = each {
// print(value)
// }
//}
I want to be able to treat each member of the collection, and extract its flag, and then make a decision, based on that flag.
I know that I could do it with a big ol' switch statement, with all the enum values (second go), but it would be nice if there were some generic way to just access all the values.
You would still have to test against the each case, but you could do
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
switch each {
case .valueA(let val), .valueB(let val), .valueC(let val):
print(val)
}
}
This would still apply if you have cases with associated values of different types:
enum Cases {
case one(Int)
case two(Int)
case three(Int, String)
case four(String)
case five(String)
}
let testArray = [Cases.one(1), .two(2), .three(3, "three"),
.four("four"), .five("five")]
// Matching associated Ints
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal), .three(let intVal, _):
print(intVal)
case .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching associated Strings
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal):
print(intVal)
case .three(_, let strVal), .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching any type, cast to Any
for value in testArray {
switch value {
case .one(let anyVal as Any), .five(let anyVal as Any):
print(anyVal)
default:
continue // skipping these cases
}
}
The important takeaway is that you are binding the same var names in every statement whose value you are trying to match, meaning you can't match values that aren't available in ever statement:
switch ... {
// This won't work, because myInt is not defined in case .two
// and myOtherInt isn't defined in case .one
case .one(let myInt), .two(let myOtherInt):
...
}
To add to #Kiril's answer, if you wanted computed values for cases with different types of associated values, you can define optional computed vars to return those values:
extension Cases {
var intValue: Int? {
switch self {
case .one(let myInt), .two(let myInt), .three(let myInt, _):
return myInt
default:
return nil
}
}
var strValue: String? {
switch self {
case .three(_, let myStr), .four(let myStr), .five(let myStr):
return myStr
default:
return nil
}
}
}
I'd suggest a slight modification on your enum:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
var flag: Int {
switch self {
case .valueA(let flag),
.valueB(let flag),
.valueC(let flag):
return flag
}
}
}
That makes any loop that just needs a flag trivial:
for each in a {
print(each.flag)
}

In Swift, why can't a 'case' conditional match an enum case with no associated data...? [duplicate]

I want to test the equality of two Swift enum values. For example:
enum SimpleToken {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssert(t1 == t2)
However, the compiler won't compile the equality expression:
error: could not find an overload for '==' that accepts the supplied arguments
XCTAssert(t1 == t2)
^~~~~~~~~~~~~~~~~~~
Do I have do define my own overload of the equality operator? I was hoping the Swift compiler would handle it automatically, much like Scala and Ocaml do.
Swift 4.1+
As #jedwidz has helpfully pointed out, from Swift 4.1 (due to SE-0185, Swift also supports synthesizing Equatable and Hashable for enums with associated values.
So if you're on Swift 4.1 or newer, the following will automatically synthesize the necessary methods such that XCTAssert(t1 == t2) works. The key is to add the Equatable protocol to your enum.
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
Before Swift 4.1
As others have noted, Swift doesn't synthesize the necessary equality operators automatically. Let me propose a cleaner (IMHO) implementation, though:
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
public func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
switch (lhs, rhs) {
case let (.Name(a), .Name(b)),
let (.Number(a), .Number(b)):
return a == b
default:
return false
}
}
It's far from ideal — there's a lot of repetition — but at least you don't need to do nested switches with if-statements inside.
Implementing Equatable is an overkill IMHO. Imagine you have complicated and large enum with many cases and many different parameters. These parameters will all have to have Equatable implemented, too. Furthermore, who said you compare enum cases on all-or-nothing basis? How about if you are testing value and have stubbed only one particular enum parameter? I would strongly suggest simple approach, like:
if case .NotRecognized = error {
// Success
} else {
XCTFail("wrong error")
}
... or in case of parameter evaluation:
if case .Unauthorized401(_, let response, _) = networkError {
XCTAssertEqual(response.statusCode, 401)
} else {
XCTFail("Unauthorized401 was expected")
}
Find more elaborate description here: https://mdcdeveloper.wordpress.com/2016/12/16/unit-testing-swift-enums/
enum MyEnum {
case none
case simple(text: String)
case advanced(x: Int, y: Int)
}
func ==(lhs: MyEnum, rhs: MyEnum) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.simple(v0), .simple(v1)):
return v0 == v1
case let (.advanced(x0, y0), .advanced(x1, y1)):
return x0 == x1 && y0 == y1
default:
return false
}
}
There seems no compiler generated equality operator for enums, nor for structs.
“If you create your own class or structure to represent a complex data model, for example, then the meaning of “equal to” for that class or structure is not something that Swift can guess for you.” [1]
To implement equality comparison, one would write something like:
#infix func ==(a:SimpleToken, b:SimpleToken) -> Bool {
switch(a) {
case let .Name(sa):
switch(b) {
case let .Name(sb): return sa == sb
default: return false
}
case let .Number(na):
switch(b) {
case let .Number(nb): return na == nb
default: return false
}
}
}
[1] See "Equivalence Operators" at https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43
Here's another option. It's mainly the same as the others except it avoids the nested switch statements by using the if case syntax. I think this makes it slightly more readable(/bearable) and has the advantage of avoiding the default case altogether.
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
extension SimpleToken {
func isEqual(st: SimpleToken)->Bool {
switch self {
case .Name(let v1):
if case .Name(let v2) = st where v1 == v2 { return true }
case .Number(let i1):
if case .Number(let i2) = st where i1 == i2 { return true }
}
return false
}
}
func ==(lhs: SimpleToken, rhs: SimpleToken)->Bool {
return lhs.isEqual(rhs)
}
let t1 = SimpleToken.Number(1)
let t2 = SimpleToken.Number(2)
let t3 = SimpleToken.Name("a")
let t4 = SimpleToken.Name("b")
t1 == t1 // true
t1 == t2 // false
t3 == t3 // true
t3 == t4 // false
t1 == t3 // false
I'm using this simple workaround in unit test code:
extension SimpleToken: Equatable {}
func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
return String(stringInterpolationSegment: lhs) == String(stringInterpolationSegment: rhs)
}
It uses string interpolation to perform the comparison. I would not recommend it for production code, but it's concise and does the job for unit testing.
Another option would be to compare the string representations of the cases:
XCTAssert(String(t1) == String(t2))
For example:
let t1 = SimpleToken.Number(123) // the string representation is "Number(123)"
let t2 = SimpleToken.Number(123)
let t3 = SimpleToken.Name("bob") // the string representation is "Name(\"bob\")"
String(t1) == String(t2) //true
String(t1) == String(t3) //false
Expanding on mbpro's answer, here's how I used that approach to check for equality of swift enums with associated values with some edge cases.
Of course you can do a switch statement, but sometimes it's nice to just check for one value in one line. You can do it like so:
// NOTE: there's only 1 equal (`=`) sign! Not the 2 (`==`) that you're used to for the equality operator
// 2nd NOTE: Your variable must come 2nd in the clause
if case .yourEnumCase(associatedValueIfNeeded) = yourEnumVariable {
// success
}
If you want to compare 2 conditions in the same if clause, you need to use the comma instead of the && operator:
if someOtherCondition, case .yourEnumCase = yourEnumVariable {
// success
}
Another approach using if case with commas, which works in Swift 3:
enum {
case kindOne(String)
case kindTwo(NSManagedObjectID)
case kindThree(Int)
static func ==(lhs: MyEnumType, rhs: MyEnumType) -> Bool {
if case .kindOne(let l) = lhs,
case .kindOne(let r) = rhs {
return l == r
}
if case .kindTwo(let l) = lhs,
case .kindTwo(let r) = rhs {
return l == r
}
if case .kindThree(let l) = lhs,
case .kindThree(let r) = rhs {
return l == r
}
return false
}
}
This is how I wrote in my project. But I can't remember where I got the idea. (I googled just now but didn't see such usage.) Any comment would be appreciated.
From Swift 4.1 just add Equatable protocol to your enum and use XCTAssert or XCTAssertEqual:
enum SimpleToken : Equatable {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssertEqual(t1, t2) // OK
t1 and t2 are not numbers, they are instances of SimpleTokens with values associated.
You can say
var t1 = SimpleToken.Number(123)
You can then say
t1 = SimpleToken.Name(“Smith”)
without a compiler error.
To retrieve the value from t1, use a switch statement:
switch t1 {
case let .Number(numValue):
println("Number: \(numValue)")
case let .Name(strValue):
println("Name: \(strValue)")
}
the 'advantage' when compare to accepted answer is, that there is no 'default' case in 'main' switch statement, so if you extend your enum with other cases, the compiler will force you to update the rest of code.
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
extension SimpleToken {
func isEqual(st: SimpleToken)->Bool {
switch self {
case .Name(let v1):
switch st {
case .Name(let v2): return v1 == v2
default: return false
}
case .Number(let i1):
switch st {
case .Number(let i2): return i1 == i2
default: return false
}
}
}
}
func ==(lhs: SimpleToken, rhs: SimpleToken)->Bool {
return lhs.isEqual(rhs)
}
let t1 = SimpleToken.Number(1)
let t2 = SimpleToken.Number(2)
let t3 = SimpleToken.Name("a")
let t4 = SimpleToken.Name("b")
t1 == t1 // true
t1 == t2 // false
t3 == t3 // true
t3 == t4 // false
t1 == t3 // false
Additionally to the answers above, you can add computed properties as helpers. It's one of the many ways to optimise readability also.
enum UserAccountViewStack: Hashable {
case notLoggedIn
case initialDevicePairing
case deviceMainView
case leftMenu(LeftMenuStack? = nil)
case rightMenu(RightMenuStack? = nil)
static var `default`: Self {
.deviceMainView
}
var isLeftMenu: Bool {
if case .leftMenu = self {
return true
}
return false
}
var isRightMenu: Bool {
if case .rightMenu = self {
return true
}
return false
}
}
In Swift 5, you can use the Equatable protocol to compare the enums like below:
enum MyEnum: Equatable {
case firstCase(String)
case secondCase(Int)
case thirdCase(Bool)
}
let enum1 = MyEnum.firstCase("hello")
let enum2 = MyEnum.firstCase("hello")
let enum3 = MyEnum.firstCase("world")
if enum1 == enum2 {
print("enum1 and enum2 are equal")
} else {
print("enum1 and enum2 are not equal")
}
if enum1 == enum3 {
print("enum1 and enum3 are equal")
} else {
print("enum1 and enum3 are not equal")
}
Here is the output:
enum1 and enum2 are equal
enum1 and enum3 are not equal
You can compare using switch
enum SimpleToken {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
switch(t1) {
case let .Number(a):
switch(t2) {
case let . Number(b):
if a == b
{
println("Equal")
}
default:
println("Not equal")
}
default:
println("No Match")
}

How can I add raw values for an enum that doesn't have it?

Given an external enum that I can't change:
enum MyEnum {
case first
case second
}
How would I best make this RawRepresentable, or at least convertible to an Int (or String) ?
I could write an extension to mimic rawValue, but this feels rather clumsy:
extension MyEnum {
enum EnumError: Error {
case invalidValue
}
init (rawValue: Int) throws {
switch rawValue {
case 0:
self = .first
case 1:
self = .second
default:
throw EnumError.invalidValue
}
}
var rawValue: Int {
switch self {
case .first:
return 0
case .second:
return 1
}
}
}
What is a better way?
This works:
enum MyEnum {
case first
case second
}
extension MyEnum {
enum MyExtendedEnum:Int {
case first
case second
}
}
Its a bit more cleaner code anyways, and your call is now:
let myVar = MyEnum.MyExtendedEnum.RawValue()

Error why matching an enumeration using a if statement [duplicate]

This question already has an answer here:
Testing for enum value fails if one has associated value?
(1 answer)
Closed 8 years ago.
I face a problem using enumeration I can't understand.
Here is declaration of an enumeration type:
enum SomeType {
case un
case deux
case trois
}
Then I want to match an individual enumeration values with a if statement:
var testValue: SomeType = .trois
if testValue == .trois {
// Do something
}
Everything is fine!
Now I want to add an associated value only to the first member value:
enum SomeType {
case un(Int)
case deux
case trois
}
var testValue: SomeType = .trois
if testValue == .trois {
// Do something
}
An error than appear on the if statement: Could not find member 'trois'
Does this mean enumerations can only be matched using a switchstatement?
Precisions
What I want to achieve is: "Does testValue is of member value of 'trois' with no consideration for associated value". In others words, how to match an enumeration on member value only.
Here a solution implementing Airspeed Velocity answers:
// Test equality only on member value
func == (lhs:SomeType, rhs:SomeType) -> Bool {
switch (lhs, rhs) {
case (.un(let lhsNum), .un(let rhsNum)):return true
case (.deux, .deux): return true
case (.trois, .trois): return true
default: return false
}
}
// Test equality on member value AND associated value
func === (lhs:SomeType, rhs:SomeType) -> Bool {
switch (lhs, rhs) {
case (.un(let lhsNum), .un(let rhsNum)) where lhsNum == rhsNum: return true
case (.deux, .deux): return true
case (.trois, .trois): return true
default: return false
}
}
var testValue = SomeType.un(3)
// Tests
if testValue == .un(1) {
println("Same member value")
}
if testValue === .un(3) {
println("Same member value AND same associated contents")
}
Enums that don't have associated types are automatically equatable. Enums that have associated types aren't. This makes sense, because only you can know how the associated type (such as the integer that comes with your .un value) should be handled. Even though .trois doesn't have an associated type, the lack of freebie equateableness affects the whole enum. Switch works a little differently, using pattern matching, so it still works.
If you want your enum with an associated type to be equatable, you can define your own == operator:
enum SomeType {
case un(Int)
case deux
case trois
}
// possibly there's a more succinct way to do this switch
func ==(lhs: SomeType, rhs: SomeType) -> Bool {
switch (lhs,rhs) {
case let (.un(i), .un(j)) where i == j: return true
case (.deux,.deux): return true
case (.trois, .trois): return true
default: return false
}
}
var testValue: SomeType = .trois
if testValue == .trois {
println("equals .trois")
}
// note, for SomeType to work with generic
// functions that require Equatable, you have
// to add that too:
extension SomeType: Equatable { }
// which means this will work:
let a: [SomeType] = [.un(1), .deux, .trois]
find(a, .trois)

How to test equality of Swift enums with associated values

I want to test the equality of two Swift enum values. For example:
enum SimpleToken {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssert(t1 == t2)
However, the compiler won't compile the equality expression:
error: could not find an overload for '==' that accepts the supplied arguments
XCTAssert(t1 == t2)
^~~~~~~~~~~~~~~~~~~
Do I have do define my own overload of the equality operator? I was hoping the Swift compiler would handle it automatically, much like Scala and Ocaml do.
Swift 4.1+
As #jedwidz has helpfully pointed out, from Swift 4.1 (due to SE-0185, Swift also supports synthesizing Equatable and Hashable for enums with associated values.
So if you're on Swift 4.1 or newer, the following will automatically synthesize the necessary methods such that XCTAssert(t1 == t2) works. The key is to add the Equatable protocol to your enum.
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
Before Swift 4.1
As others have noted, Swift doesn't synthesize the necessary equality operators automatically. Let me propose a cleaner (IMHO) implementation, though:
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
public func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
switch (lhs, rhs) {
case let (.Name(a), .Name(b)),
let (.Number(a), .Number(b)):
return a == b
default:
return false
}
}
It's far from ideal — there's a lot of repetition — but at least you don't need to do nested switches with if-statements inside.
Implementing Equatable is an overkill IMHO. Imagine you have complicated and large enum with many cases and many different parameters. These parameters will all have to have Equatable implemented, too. Furthermore, who said you compare enum cases on all-or-nothing basis? How about if you are testing value and have stubbed only one particular enum parameter? I would strongly suggest simple approach, like:
if case .NotRecognized = error {
// Success
} else {
XCTFail("wrong error")
}
... or in case of parameter evaluation:
if case .Unauthorized401(_, let response, _) = networkError {
XCTAssertEqual(response.statusCode, 401)
} else {
XCTFail("Unauthorized401 was expected")
}
Find more elaborate description here: https://mdcdeveloper.wordpress.com/2016/12/16/unit-testing-swift-enums/
enum MyEnum {
case none
case simple(text: String)
case advanced(x: Int, y: Int)
}
func ==(lhs: MyEnum, rhs: MyEnum) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.simple(v0), .simple(v1)):
return v0 == v1
case let (.advanced(x0, y0), .advanced(x1, y1)):
return x0 == x1 && y0 == y1
default:
return false
}
}
There seems no compiler generated equality operator for enums, nor for structs.
“If you create your own class or structure to represent a complex data model, for example, then the meaning of “equal to” for that class or structure is not something that Swift can guess for you.” [1]
To implement equality comparison, one would write something like:
#infix func ==(a:SimpleToken, b:SimpleToken) -> Bool {
switch(a) {
case let .Name(sa):
switch(b) {
case let .Name(sb): return sa == sb
default: return false
}
case let .Number(na):
switch(b) {
case let .Number(nb): return na == nb
default: return false
}
}
}
[1] See "Equivalence Operators" at https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43
Here's another option. It's mainly the same as the others except it avoids the nested switch statements by using the if case syntax. I think this makes it slightly more readable(/bearable) and has the advantage of avoiding the default case altogether.
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
extension SimpleToken {
func isEqual(st: SimpleToken)->Bool {
switch self {
case .Name(let v1):
if case .Name(let v2) = st where v1 == v2 { return true }
case .Number(let i1):
if case .Number(let i2) = st where i1 == i2 { return true }
}
return false
}
}
func ==(lhs: SimpleToken, rhs: SimpleToken)->Bool {
return lhs.isEqual(rhs)
}
let t1 = SimpleToken.Number(1)
let t2 = SimpleToken.Number(2)
let t3 = SimpleToken.Name("a")
let t4 = SimpleToken.Name("b")
t1 == t1 // true
t1 == t2 // false
t3 == t3 // true
t3 == t4 // false
t1 == t3 // false
I'm using this simple workaround in unit test code:
extension SimpleToken: Equatable {}
func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
return String(stringInterpolationSegment: lhs) == String(stringInterpolationSegment: rhs)
}
It uses string interpolation to perform the comparison. I would not recommend it for production code, but it's concise and does the job for unit testing.
Another option would be to compare the string representations of the cases:
XCTAssert(String(t1) == String(t2))
For example:
let t1 = SimpleToken.Number(123) // the string representation is "Number(123)"
let t2 = SimpleToken.Number(123)
let t3 = SimpleToken.Name("bob") // the string representation is "Name(\"bob\")"
String(t1) == String(t2) //true
String(t1) == String(t3) //false
Expanding on mbpro's answer, here's how I used that approach to check for equality of swift enums with associated values with some edge cases.
Of course you can do a switch statement, but sometimes it's nice to just check for one value in one line. You can do it like so:
// NOTE: there's only 1 equal (`=`) sign! Not the 2 (`==`) that you're used to for the equality operator
// 2nd NOTE: Your variable must come 2nd in the clause
if case .yourEnumCase(associatedValueIfNeeded) = yourEnumVariable {
// success
}
If you want to compare 2 conditions in the same if clause, you need to use the comma instead of the && operator:
if someOtherCondition, case .yourEnumCase = yourEnumVariable {
// success
}
Another approach using if case with commas, which works in Swift 3:
enum {
case kindOne(String)
case kindTwo(NSManagedObjectID)
case kindThree(Int)
static func ==(lhs: MyEnumType, rhs: MyEnumType) -> Bool {
if case .kindOne(let l) = lhs,
case .kindOne(let r) = rhs {
return l == r
}
if case .kindTwo(let l) = lhs,
case .kindTwo(let r) = rhs {
return l == r
}
if case .kindThree(let l) = lhs,
case .kindThree(let r) = rhs {
return l == r
}
return false
}
}
This is how I wrote in my project. But I can't remember where I got the idea. (I googled just now but didn't see such usage.) Any comment would be appreciated.
From Swift 4.1 just add Equatable protocol to your enum and use XCTAssert or XCTAssertEqual:
enum SimpleToken : Equatable {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssertEqual(t1, t2) // OK
t1 and t2 are not numbers, they are instances of SimpleTokens with values associated.
You can say
var t1 = SimpleToken.Number(123)
You can then say
t1 = SimpleToken.Name(“Smith”)
without a compiler error.
To retrieve the value from t1, use a switch statement:
switch t1 {
case let .Number(numValue):
println("Number: \(numValue)")
case let .Name(strValue):
println("Name: \(strValue)")
}
the 'advantage' when compare to accepted answer is, that there is no 'default' case in 'main' switch statement, so if you extend your enum with other cases, the compiler will force you to update the rest of code.
enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
extension SimpleToken {
func isEqual(st: SimpleToken)->Bool {
switch self {
case .Name(let v1):
switch st {
case .Name(let v2): return v1 == v2
default: return false
}
case .Number(let i1):
switch st {
case .Number(let i2): return i1 == i2
default: return false
}
}
}
}
func ==(lhs: SimpleToken, rhs: SimpleToken)->Bool {
return lhs.isEqual(rhs)
}
let t1 = SimpleToken.Number(1)
let t2 = SimpleToken.Number(2)
let t3 = SimpleToken.Name("a")
let t4 = SimpleToken.Name("b")
t1 == t1 // true
t1 == t2 // false
t3 == t3 // true
t3 == t4 // false
t1 == t3 // false
Additionally to the answers above, you can add computed properties as helpers. It's one of the many ways to optimise readability also.
enum UserAccountViewStack: Hashable {
case notLoggedIn
case initialDevicePairing
case deviceMainView
case leftMenu(LeftMenuStack? = nil)
case rightMenu(RightMenuStack? = nil)
static var `default`: Self {
.deviceMainView
}
var isLeftMenu: Bool {
if case .leftMenu = self {
return true
}
return false
}
var isRightMenu: Bool {
if case .rightMenu = self {
return true
}
return false
}
}
In Swift 5, you can use the Equatable protocol to compare the enums like below:
enum MyEnum: Equatable {
case firstCase(String)
case secondCase(Int)
case thirdCase(Bool)
}
let enum1 = MyEnum.firstCase("hello")
let enum2 = MyEnum.firstCase("hello")
let enum3 = MyEnum.firstCase("world")
if enum1 == enum2 {
print("enum1 and enum2 are equal")
} else {
print("enum1 and enum2 are not equal")
}
if enum1 == enum3 {
print("enum1 and enum3 are equal")
} else {
print("enum1 and enum3 are not equal")
}
Here is the output:
enum1 and enum2 are equal
enum1 and enum3 are not equal
You can compare using switch
enum SimpleToken {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
switch(t1) {
case let .Number(a):
switch(t2) {
case let . Number(b):
if a == b
{
println("Equal")
}
default:
println("Not equal")
}
default:
println("No Match")
}