class MyClass
{
enum MyEnum {
case FirstCase
case SecondCase(Int)
case ThirdCase
}
var state:MyEnum!
func myMethod ()
{
if state! == MyEnum.FirstCase {
// Do something
}
}
}
I get the compiler error pointing at the if statement::
Binary operator '==' cannot be applied to two 'MyClass.MyEnum'
operands
If instead, I use a switch statement, there is no problem:
switch state! {
// Also, why do I need `!` if state is already an
// implicitly unwrapped optional? Is it because optionals also
// are internally enums, and the compiler gets confused?
case .FirstCase:
// do something...
default:
// (do nothing)
break
}
However, the switch statement feels too verbose: I just want to do something for .FirstCase, and nothing otherwise. An if statement makes more sense.
What's going on with enums and == ?
EDIT: This is ultra-weird. After settling for the switch version and moving on to other (totally unrelated) parts of my code, and coming back, the if-statement version (comnparing force-unwrapped property against fixed enum case) is compiling with no errors.
I can only conclude that it has something to do with some corrupted cache in the parser that got cleared along the way.
EDIT 2 (Thanks #LeoDabus and #MartinR): It seems that the error appears when I set an associated value to the other enum case (not the one I am comparing against - in this case, .SecondCase). I still don't understand why that triggers this compiler error in particular ("Can't use binary operator '=='..."), or what that means.
As you said in a comment, your enumeration type actually has associated
values. In that case there is no default == operator for the enum type.
But you can use pattern matching even in an if statement (since Swift 2):
class MyClass {
enum MyEnum {
case FirstCase
case SecondCase
case ThirdCase(Int)
}
var state:MyEnum!
func myMethod () {
if case .FirstCase? = state {
}
}
}
Here .FirstCase? is a shortcut for .Some(MyEnum.FirstCase).
In your switch-statement, state is not automatically unwrapped,
even if it is an implicitly unwrapped optional (otherwise you could
not match against nil). But the same pattern can be used here:
switch state {
case .FirstCase?:
// do something...
default:
break
}
Update: As of Swift 4.1 (Xcode 9.3) the compiler can synthesize conformance to Equatable/Hashable for enums with associated values (if all their types are Equatable/Hashable). It suffices to declare the conformance:
class MyClass {
enum MyEnum: Equatable {
case firstCase
case secondCase
case thirdCase(Int)
}
var state:MyEnum!
func myMethod () {
if state == .firstCase {
// ...
}
}
}
class MyClass {
enum MyEnum {
case FirstCase
case SecondCase
case ThirdCase
}
var state: MyEnum!
func myMethod() {
guard let state = state else { return }
if state == MyEnum.FirstCase {
// Do something
print(true)
} else {
print(false)
}
}
}
let myClass = MyClass()
myClass.state = .FirstCase
myClass.myMethod()
myClass.state = .SecondCase
myClass.myMethod()
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
Note that I have read this post but that post uses a switch statement and it is supposed to do something (return true) when the pattern matches. I, on the other hand, don't want to do anything if the pattern matches and use an if-case statement.
I have this enum:
enum MyEnum {
case a
case b(Int)
case c
case d
}
Here's an instance of it:
let myEnum: MyEnum = .a
Now I want to do something if myEnum is not .b. Since .b has an associated value, I can't simply use an if statement check:
if myEnum != .b { // compiler error
// do my thing here
}
So I must use an if-case statement to pattern match it:
if case .b(_) = myEnum {
} else {
// do my thing here
}
But I really hate the use of the empty if clause. That just looks unswifty to me. I tried to naïvely do this:
if case .b(_) != myEnum { // compiler error!
// do my thing here
}
Is there a better way to do this other than using an empty if clause?
I still have code that should run regardless of whether the pattern matches, so a guard statement won't work.
This is purely minimal semantic change of your own code, but note that you can simply "discard" the empty if clause inline with the case pattern matching:
if case .b(_) = myEnum {} else {
// do your thing here
}
or, leaving out the redundant pattern matching for the associated value of case b:
if case .b = myEnum {} else {
// do your thing here
}
This looks somewhat like a guard clause, but without exiting the scope.
You could use a guard:
guard case .b = myEnum else {
// do your stuff here
return
}
The downside is that you have to exit the scope...
What about:
switch myEnum {
case .b(_):
break
default:
// do your thing here
}
Create a var on the enum which calculates if your value is not .b(_):
enum MyEnum {
case a
case b(Int)
case c
case d
var notB: Bool {
switch self {
case .b(_):
return false
default:
return true
}
}
}
MyEnum.a.notB // true
MyEnum.b(1).notB // false
MyEnum.c // true
MyEnum.d // true
Not the greatest answer since there is still a lot of code to do the check, but at least the check is only one line when you actually use it.
You can write a computed property and have it return a bool value depending on case
enum MyEnum {
case a
case b(Int)
case c
var isCaseB: Bool {
switch self {
case .b(_):
return true
default:
return false
}
}
}
then in your code cleanly check:
if !enumVal.isCaseB {
}
I checked the answer you mentioned in your question but I wasn't sure if you meant you didn't want to use a switch statement at all or just that you didn't want to mix it in with your other code. I think this is a nice and clean way to check prior to writing whatever implementation you need to do depending on the case.
'emun' seems to me like a keyword or a primitive type.
And obviously following code does not compile:
if self is enum {
}
But how can I be able to check if certain protocol is implemented by any enum?
protocol Enumatable {
}
extension Enumatable {
func isEnum() -> Bool {
return self is enum //it does not compile
}
}
But what I really want is to set some kind of constraint in the protocol to force the adopting class to be an enum. Is that possible?
Thanks!
I'm not sure how performant it is to use Mirrors. But here you go:
enum SomeEnum {
case one
case two
}
let mirror = Mirror(reflecting: SomeEnum.one)
if let displayStyle = mirror.displayStyle {
switch displayStyle {
case .enum:
print("I am an enum")
default:
print("not an enum")
}
}
I'm playing around with Swift enums and wondering if there is a way to change the assoicated value of an enum such as the code below attempts but fails.
enum myEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(let a):
if a != nil {
// a = 5 can't do this of course as assigning to let
var temp = a!; // but this generates a warning
temp = 5;
}
}
}
}
If this isn't currently possible, will it be when mutating enums come to XCode?
The whole enum is a single value, so you have to replace it entirely, not field by field. But you can certainly do that by replacing self.
enum MyEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(_):
self = .SomeCase(5)
}
}
}
// You have to use `var` here of course or you can't mutate it.
var x = MyEnum.SomeCase(1) // .SomeCase(Optional(1))
x.someFunc() // .SomeCase(Optional(5))
Rob Napier's answer is fine but I have a different understanding of your question. I would have written something like this instead:
enum MyEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(.Some(_)):
self = .SomeCase(5)
default:
// or case .SomeCase(.None): if you prefer
break
}
}
}
With this code, the enum is mutated with someFunc if and only if its associated value is not nil. Thus, I have the following results while testing in a Playground (results appear as comments):
var x = MyEnum.SomeCase(nil)
switch x {
case .SomeCase(.Some(let a)):
println(a)
default:
println("Associated value is nil") // Will print "Associated value is nil"
}
x = MyEnum.SomeCase(20)
x.someFunc()
switch x {
case .SomeCase(.Some(let a)):
println(a) // Will print 5
default:
println("Associated value is nil")
}
I have tried to boil this issue down to its simplest form with the following.
Setup
Xcode Version 6.1.1 (6A2008a)
An enum defined in MyEnum.swift:
internal enum MyEnum: Int {
case Zero = 0, One, Two
}
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero": self = .Zero
case "one": self = .One
case "two": self = .Two
default: return nil
}
}
}
and code that initializes the enum in another file, MyClass.swift:
internal class MyClass {
let foo = MyEnum(rawValue: 0) // Error
let fooStr = MyEnum(string: "zero")
func testFunc() {
let bar = MyEnum(rawValue: 1) // Error
let barStr = MyEnum(string: "one")
}
}
Error
Xcode gives me the following error when attempting to initialize MyEnum with its raw-value initializer:
Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'
Notes
Per the Swift Language Guide:
If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value’s type (as a parameter called rawValue) and returns either an enumeration member or nil.
The custom initializer for MyEnum was defined in an extension to test whether the enum's raw-value initializer was being removed because of the following case from the Language Guide. However, it achieves the same error result.
Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. [...]
If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation.
Moving the enum definition to MyClass.swift resolves the error for bar but not for foo.
Removing the custom initializer resolves both errors.
One workaround is to include the following function in the enum definition and use it in place of the provided raw-value initializer. So it seems as if adding a custom initializer has a similar effect to marking the raw-value initializer private.
init?(raw: Int) {
self.init(rawValue: raw)
}
Explicitly declaring protocol conformance to RawRepresentable in MyClass.swift resolves the inline error for bar, but results in a linker error about duplicate symbols (because raw-value type enums implicitly conform to RawRepresentable).
extension MyEnum: RawRepresentable {}
Can anyone provide a little more insight into what's going on here? Why isn't the raw-value initializer accessible?
This bug is solved in Xcode 7 and Swift 2
extension TemplateSlotType {
init?(rawString: String) {
// Check if string contains 'carrousel'
if rawString.rangeOfString("carrousel") != nil {
self.init(rawValue:"carrousel")
} else {
self.init(rawValue:rawString)
}
}
}
In your case this would result in the following extension:
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero":
self.init(rawValue:0)
case "one":
self.init(rawValue:1)
case "two":
self.init(rawValue:2)
default:
return nil
}
}
}
You can even make the code simpler and useful without switch cases, this way you don't need to add more cases when you add a new type.
enum VehicleType: Int, CustomStringConvertible {
case car = 4
case moped = 2
case truck = 16
case unknown = -1
// MARK: - Helpers
public var description: String {
switch self {
case .car: return "Car"
case .truck: return "Truck"
case .moped: return "Moped"
case .unknown: return "unknown"
}
}
static let all: [VehicleType] = [car, moped, truck]
init?(rawDescription: String) {
guard let type = VehicleType.all.first(where: { description == rawDescription })
else { return nil }
self = type
}
}
Yeah this is an annoying issue. I'm currently working around it using a global-scope function that acts as a factory, i.e.
func enumFromString(string:String) -> MyEnum? {
switch string {
case "One" : MyEnum(rawValue:1)
case "Two" : MyEnum(rawValue:2)
case "Three" : MyEnum(rawValue:3)
default : return nil
}
}
This works for Swift 4 on Xcode 9.2 together with my EnumSequence:
enum Word: Int, EnumSequenceElement, CustomStringConvertible {
case apple, cat, fun
var description: String {
switch self {
case .apple:
return "Apple"
case .cat:
return "Cat"
case .fun:
return "Fun"
}
}
}
let Words: [String: Word] = [
"A": .apple,
"C": .cat,
"F": .fun
]
extension Word {
var letter: String? {
return Words.first(where: { (_, word) -> Bool in
word == self
})?.key
}
init?(_ letter: String) {
if let word = Words[letter] {
self = word
} else {
return nil
}
}
}
for word in EnumSequence<Word>() {
if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs {
print("\(letter) for \(word)")
}
}
Output
A for Apple
C for Cat
F for Fun
Add this to your code:
extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}