How can I validate equal with below source code?
enum ErrorAPI: Equatable, Error {
case CannotFetch(String)
}
func ==(lhs: ErrorAPI, rhs: ErrorAPI) -> Bool {
switch (lhs, rhs) {
case (.CannotFetch(let a), .CannotFetch(let b)) where a == b:
return true
default:
return false
}
}
The enum case CannotFetch has an associated value of type String. That means, when the enum case is set to CannotFetch, a specific String is associated with that case. Read through the official Swift documentation about Enumerations if you need to understand Associated Values.
The func ==() method is a so called Equivalence Operator. You can read more about it in the official Swift documentation about Advanced Operators.
When comparing two enums, lets call them enumOne and enumTwo, this function is implemented to be able to compare those two enums if both cases are CannotFetch(String).
Example:
let enumOne = ErrorAPI.CannotFetch("Hi")
let enumTwo = ErrorAPI.CannotFetch("Hello")
if enumOne != enumTwo {
print("They are not equal!")
} else {
print("They are equal")
}
The line case (.CannotFetch(let a), .CannotFetch(let b)) where a == b: works as follows:
This case is only proceeded if both, enumOne and enumTwo, are set to the case CannotFetch(String)
Take the associated value (=String) of the left hand sided enum, i.e. "Hi" and assign it to the new constant let a.
Take the associated value (=String) of the left hand sided enum, i.e. "Hello" and assign it to the new constant let b.
Additionally, Check if the values behind the constants a and b are equal, i.e. Check if the Strings Hi and Hello are equal.
If all this is true, execute the code block in the case, i.e. return true
Related
I'd like to assert whether a value is a specific enum case.
For example, if I have the following enum class, and a variable let value: MyEnum:
enum MyEnum {
case firstCase(value: Int)
case secondCase
}
I'd like to check whether value is an instance of firstCase.
In essence, I'd like to be able to write the following or something equivalent:
let value: MyEnum = .firstCase(value: 3)
XCTAssertEnumCase(value, .firstCase)
How can I achieve this? I'm looking for an already existing XCT function, or for instructions how to write XCTAssertEnumCase myself.
You can easily create a function that works for specific enums, however, creating a generic assert function that works for any enums will be quite hard to achieve, because there's no protocol/type constraint that could represent any enum. You can use RawRepresentable for enums with raw values, but that won't cover all enums, such as the one in your question.
This is the function for your specific enum.
func XCTAssertEnumCase(_ testValue: MyEnum, _ expectedValue: MyEnum) -> Bool {
switch (testValue, expectedValue) {
case (.firstCase, .firstCase):
return true
case (.secondCase, .secondCase):
return true
default:
return false
}
}
Alternatively, you can make your enum conform to Equatable in your test target (if it doesn't already conform in your actual production target) and only check case equality in your Equatable conformance, but then you won't be able to easily test "full equality" (including the associated values). Also, the solution will require you to manually implement Equatable conformance for all protocols that you are testing.
You cannot instantiate an enum case that has an associated value without actually supplying an associated value. So XCTAssertEnumCase(value, .firstCase) cannot be achieved.
You can do XCTAssertEnumCase(testValue, .firstCase(value: 3313) where you can pass in any Int to the associated value of firstCase and as long as testValue is also firstCase, the func will return true, regardless of the associated values.
Alternatively, you could create separate functions for asserting each case of your enum.
extension MyEnum {
func assertFirstCase() -> Bool {
switch self {
case .firstCase:
return true
default:
return false
}
}
func assertSecondCase() -> Bool {
switch self {
case .secondCase:
return true
default:
return false
}
}
}
And then use it like this:
let value: MyEnum = .firstCase(value: 3)
value.assertFirstCase() // returns true
value.assertSecondCase() // returns false
I want to get the associated value of swift enum object, is there a way to do it shorter/better than in switch statement below?
enum Test {
case a(Int), b(Int), c(Int)
}
func printValue(_ t: Test) {
switch t {
case .a(let v), .b(let v), .c(let v): print("value \(v)")
}
}
Your code for extracting the associated value from multiple enums is the most economical and easy-to-read, there's no need to improve it.
However, the fact that you are looking to extract an associated value regardless of enum's case suggests that you are not using associated values correctly: rather than associating a value with each individual case, you should create a composite type that holds the Int and an enum without an associated value, i.e.
enum Test {
case a, b, c
}
class MyClass {
var num : Int
var tst : Test
}
Now that the associated value is "outside" each enum element, it can be accessed independently of the case, and you can also give it a meaningful name, which adds to readability of your program.
You might want to use mirror type - it's not the better way, but it can be helpful in some cases:
enum Test {
case a(Int), b(Int), c(Int)
}
func printValue(_ t: Test) {
let mirror = Mirror(reflecting: t)
print(mirror.children.first?.value ?? "")
}
printValue(.a(15))
Also using if/case like this, it's a shorter way if you need to extract value only from one case, sometimes it's helpful:
if case .a(let val) = t {
print("value \(val)")
}
Or may be raw value will fit better for your case:
enum Test: Int {
case a = 1
case b = 2
case c = 5
}
func printValue(_ t: Test) {
print("value \(t.rawValue)")
}
printValue(.a)
Is it possible to create a Generic enum function that returns a boolean if an enum instance is an enum Type or does this already exist and I don't know about it?
I use a ton of enums in my projects. Very often I define enums with associated values.
Simple Example:
enum Mode {
case new
case edit(Record) // Record is a struct type
}
I regularly check whether an enum instance is a specific enum case. Many times, however, I don't need to check the associated value. I'm looking for a convenient way to check the case. Each method I know about has a downside.
Method 1-2: If Case pattern matching or Switch case
let myMode = Mode.edit
if case Mode.edit(_) = myMode {
// do something
}
switch mode {
case .edit:
// do something
default:
break
}
Downsides:
Cannot assign the check to a variable directly. Must do so in closure
cannot use this pattern directly as an argument to a function
Method 3-5 Implement an enum function or check value of computed property or Equatable protocol
Downsides:
must be implemented for each enum type
Instead, I'm looking for a way to write a Generic function that returns a boolean if a enum instance matches an enum case. Something that can be written once and be applied to all enums. Similar to Generic function for Struct and Class Types:
func checkType<T, S> (a: T, _: S.Type) -> Bool {
return a is S // though you could just call this directly
}
I don't think there is a good idiomatic way of achieving this. The only thing that comes to mind is to compare the raw memory of your enum instance against a dummy instance with the desired case.
As we don't care about associated values we would only need to require their respective last byte to be identical.
func unsafeEqualityLastByteOnly<A>(_ lhs: A, _ rhs: A) -> Bool {
var (lhs, rhs) = (lhs, rhs)
let offset = MemoryLayout<A>.size - 1
return withUnsafePointer(to: &lhs) { lhsPtr in
withUnsafePointer(to: &rhs) { rhsPtr in
let lhsPtr = unsafeBitCast(lhsPtr, to: UnsafeRawPointer.self)
let rhsPtr = unsafeBitCast(rhsPtr, to: UnsafeRawPointer.self)
return memcmp(lhsPtr.advanced(by: offset), rhsPtr.advanced(by: offset), 1) == 0
}
}
}
This is far from pretty, but it works.
enum Test {
case a(Int)
case b(Int)
}
let a1 = Test.a(1)
let a2 = Test.a(2)
let b1 = Test.b(1)
let b2 = Test.b(2)
unsafeEqualityLastByteOnly(a1, a1) // true
unsafeEqualityLastByteOnly(a1, a2) // true
unsafeEqualityLastByteOnly(a2, a2) // true
unsafeEqualityLastByteOnly(b1, b1) // true
unsafeEqualityLastByteOnly(b1, b2) // true
unsafeEqualityLastByteOnly(b2, b2) // true
unsafeEqualityLastByteOnly(a1, b1) // false
unsafeEqualityLastByteOnly(a1, b2) // false
unsafeEqualityLastByteOnly(a2, b1) // false
unsafeEqualityLastByteOnly(a2, b2) // false
Use your own judgement to decide whether or not this is something you want in your project. It's obviously not a technique that should be recommended without any reservations.
I wonder which type of value can be compared in switch statement. The official document said:
Cases can match many different patterns, including interval matches, tuples, and casts to a specific type
Is there anything else? Can I compare class type in switch statement?
Suppose I hava a class A:
class A {
}
func == (lhs: A, rhs: A) -> Bool { return true }
Then I can check if two objects of class A are equal. But I still can't do like this:
var a1 = A(); var a2 = A()
switch a1 {
case a2: //do something
}
Although we rarely write codes like these, I'm still very curious about how switch statement works in Swift.
As explained in Expression Patterns,
The expression represented by the expression pattern is compared with the value of an input expression using the Swift standard library ~= operator.
You can define func ~=(lhs: A, rhs: A) if you wish for your custom type to be used in a switch statement.
But I'd also recommend simply using the Equatable protocol, implementing ==, and then you can write if a1 == a2 { ... }.
In fact, the standard library provides
public func ~=<T : Equatable>(a: T, b: T) -> Bool
So if you conform to Equatable, you don't need to provide your own ~=.
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.