How to test equality of Swift enums with associated values - swift

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

Related

Filtering an array based on a Swift enum with an associated value - w/o mentioning the associated value

I have an enum with associated value for some of the cases:
enum Foo {
case a
case b
case c(String?)
}
I also have a struct with this enum as a variable
struct Bar {
var foo: Foo
}
I then have an array of this objects
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
I want to create a function that operates on a subset of this array, based on the cases it receives something like
func printOnly(objectsWithCase: Foo)
So far its pretty simple, but now here's the catch: for this operation I WANT TO IGNORE the associated value.
I would like to make this function be able to take .c case without mentioning an associated value, as if to say "give me the ones with .c regardless of the associated values".
I other words - I'd like to pass in something like .c(_) (this doesn't work of course) and have it return (print in this case) both Bar(foo: .c(nil)) and Bar(foo: .c("someString"))
So far, I only came up with changing the functions declaration to take the filtering closure instead of the cases like this:
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
I'm wondering if there's a way to do this in Swift, while passing the cases and not the condition block ?
You can use the underscore as a wild card in pattern matching operations:
array.filter {
switch $0.foo {
case .a: return true // keep a
case .b: return false // reject b
case .c(_): return true // keep c, regardless of assoc. value.
}
}
While this is not technically what you ask for (I don't think there's any way to achieve this with enums), you can write a "fake" enum that contains a wildcard c that will match anything you want. This will give you the exact same syntax.
1) Replace Foo with the following
struct Foo: Equatable {
let rawValue: String
let associatedObject: String?
let isWildcard: Bool
fileprivate init(rawValue: String, associatedObject: String?, isWildcard: Bool) {
self.rawValue = rawValue
self.associatedObject = associatedObject
self.isWildcard = isWildcard
}
static var a: Foo {
return Foo(rawValue: "a", associatedObject: nil, isWildcard: false)
}
static var b: Foo {
return Foo(rawValue: "b", associatedObject: nil, isWildcard: false)
}
static var c: Foo {
return Foo(rawValue: "c", associatedObject: nil, isWildcard: true)
}
static func c(_ value: String?) -> Foo {
return Foo(rawValue: "c", associatedObject: value, isWildcard: false)
}
}
func ==(left: Foo, right: Foo) -> Bool {
// Match rawValue + associatedObject unless we have a wildcard
return (left.rawValue == right.rawValue)
&& (left.associatedObject == right.associatedObject || left.isWilcard || right.isWildcard)
}
2) Implement your printOnly function with ==
func printOnly(objects: [Bar], with match: Foo) {
objects.filter { $0.foo == match }.forEach { print($0) }
}
3) Success
printOnly(objects: array, with: .c) // [.c(nil), .c("someString")]
Discussion
The main drawback of this method, besides the additional boilerplate code, is that you are forced to create an enum value that should not be allowed. This method puts the responsibility on you to use it only as a wildcard, and not as a real enum value. It will also not guarantee you that other enum cases cannot be created, although you should be able to mitigate that by making the only initializer fileprivate.
Otherwise, this gives you exactly the same interface and features an enum would give you, you can define your cases just as before
let array = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("Hello")]
Finally, you can still use it inside a switch, except you will always need to add a default statement.
switch Foo.c("Hello") {
case .a:
print("A")
case .b:
print("B")
case .c: // will match .c(nil) and .c("someString")
print("C")
default:
break
}
//: Playground - noun: a place where people can play
enum Foo {
case a
case b
case c(String?)
}
struct Bar {
var foo: Foo
}
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
printArray(array: array) { bar in
switch bar.foo {
case .c:
return true
default:
return false
}
}
or
printArray(array: array) { bar in
if case let Foo.c = bar.foo {
return true
}
return false
}
EDIT
//: Playground - noun: a place where people can play
enum Foo: Equatable {
case a
case b
case c(String?)
}
func ==(lhs: Foo, rhs: Foo) -> Bool {
switch (lhs, rhs) {
case (.a, .a), (.b, .b), (.c, .c):
return true
default:
return false
}
}
struct Bar {
var foo: Foo
}
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
func printOnly(objectsWithCase wantedCase: Foo) {
printArray(array: array) { bar in
if wantedCase == bar.foo {
return true
} else {
return false
}
}
}
printOnly(objectsWithCase:.c(nil))

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

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

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

Cannot compare two enums if there is at least one case with associated value

Suppose I have:
enum Type {
case A
case B
}
Now I can compare them:
let enums = [Type.A, Type.B]
if enums[1] == Type.A { //true }
Everything is fine unless I add another case with associated value:
case C(String)
Now I have an error:
Binary operator == cannot be applied to two Type operands.
How to do this to make it working?
Unfortunately as of Xcode 7 beta 4 you have to implement your own == function yourself since the compiler can no longer infer an equality operation. Especially for Type.C: should it also check for equality of the associated value?
Examples of a logical/static implementation:
// comparing associated values
func ==(t1: Type, t2: Type) -> Bool {
switch (t1, t2) {
case (.A, .A): return true
case (.B, .B): return true
case (.C(let x), .C(let y)) where x == y: return true
default: return false
}
}
// without comparing
func ==(t1: Type, t2: Type) -> Bool {
switch (t1, t2) {
case (.A, .A): return true
case (.B, .B): return true
case (.C, .C): return true
default: return false
}
}
An easier way would be to convert it into a String (using reflection). This would be a dynamic approach/solution but it could change over time especially for own types (no longterm solution):
// you "have to" compare associated values
func ==(t1: Type, t2: Type) -> Bool {
return String(t1) == String(t2)
}
Very easy to do in Swift 4.2
enum SomeType: Equatable {
case a
case b
case c(String)
}
let enums = [SomeType.a, SomeType.b, SomeType.c("Some string")]
if enums[1] == SomeType.b { print("true") }
You'll have to implement func == for the enum type. There are examples on stackoverflow showing you how to do it with one line of code per case.

equality operator overloading in swift enums with associated values

I know an almost similar question was asked earlier, but I am not able to comment on it, because I am to new here. That is the reason why I am posting a separat question. Also my question is an extension to the previous question asked and is aimed at a more general solution. That is the link to the previous question: How to test equality of Swift enums with associated values
I want to test equality in an enum with associated values:
enum MyEnum {
case None
case Error
case Function(Int) // it is a custom type but for briefness an Int here
case ...
}
I tried to setup an overloading function as the following
func ==(a: MyEnum, b: MyEnum) -> Bool {
switch (a,b) {
case (.Function(let aa), .Function(let bb)):
if (aa==bb) {
return true
} else {
return false
}
default:
if (a == b) {
return true
} else {
return false
}
}
}
This gives no compile time error, but fails with bad_exec in the running process. Most likely because testing a==b in the default case, calls the function itself again. The .Function part works as expected, but not the rest... So the case list is somewhat longer, how can I test the cases which do not have an associated value with them for equality?
In your implementation,
if (a == b) {
recursively calls the same == function again. This eventually crashes with a stack overflow.
A working implementation would for example be:
func ==(a: MyEnum, b: MyEnum) -> Bool {
switch (a,b) {
case (.Function(let aa), .Function(let bb)):
return aa == bb
case (.Error, .Error):
return true
case (.None, .None):
return true
default:
return false
}
}
Although memcmp works:
func ==(var a: MyEnum, var b: MyEnum) -> Bool {
switch (a,b) {
case (.Function(let aa), .Function(let bb)):
return aa == bb
default:
return memcmp(&a, &b, sizeof(MyEnum)) == 0
}
}
I don't recommend this :)