How to Use Case Enum Comparison as a Boolean Expression

Can I use case enum comparison as a boolean expression?

Unfortunately, you cannot (directly) use a case condition as a Bool expression. They are only accessible in statements such as if, guard, while, for etc.

This is detailed in the language grammar, with:

case-condition → case ­pattern­ initializer­

This is then used in a condition:

condition → expression |­ availability-condition |­ case-condition­ | optional-binding-condition

(where expression represents a Bool expression)

This is then used in condition-list:

condition-list → condition­ | condition ­, ­condition-list
­

which is then used in statements such as if:

if-statement → if­ condition-list­ code-block­ else-clause­ opt­

So you can see that unfortunately case-condition is not an expression, rather just a special condition you can use in given statements.

To pack it into an expression, you'll either have to use an immediately-evaluated closure:

return { if case .active = session.state { return true }; return false }()

Or otherwise write convenience computed properties on the enum in order to get a Bool in order to check for a given case, as shown in this Q&A.

Both of which are quite unsatisfactory. This has been filed as an improvement request, but nothing has come of it yet (at the time of posting). Hopefully it's something that will be possible in a future version of the language.

Can you (now? Swift5?) write if case as a boolean when determining an enum's associated type?

Enums with associated values are equatable if you declare them Equatable. Here's the state of play:

enum MyEnum : Equatable {
case hey
case ho
case heyNonnyNo(String)
}
let e = MyEnum.hey
e == .hey // true
e == .ho // false
// e == .heyNonnyNo // blap, illegal

let e2 = MyEnum.heyNonnyNo("hello")
e2 == .heyNonnyNo("hello") // true
e2 == .heyNonnyNo("goodbye") // true

Why is e == .heyNonnyNo illegal? Because it's unclear what it can mean. This case has an associated value; its value is the associated value. So we can check whether two instances of this case have the same associated value, but we can't just ask (using ==) whether an instance is some associated value of this case.

So if that's what we want to know, we are back to if case:

if case .heyNonnyNo = e2 {
print("it's a hey nonny no")
}

But you can't say that without if (for use in a conditional) because if case is the keyword; case can't exist by itself. If you really need a Bool, you could write it out like this:

let ok : Bool = {
switch e2 {
case .heyNonnyNo: return true
default: return false
}
}()

Make an enum with two cases convertible to Bool in Swift

You can achieve this by overloading your validate function to also return Bool.

enum Validity {
case valid
case invalid(reason: String)
}

// Example validate function
func validate(_ string: String) -> Validity {
if string.count < 5 {
return .valid
} else {
return .invalid(reason: "String too long")
}
}

// Overloaded function which returns Bool
func validate(_ string: String) -> Bool {
let validity: Validity = validate(string)
if case .valid = validity {
return true
} else {
return false
}
}

// Swift expects the result of validate to be a Bool, so it calls the
// (String) -> Bool version of validate
let b1: Bool = validate("this")
print(b1)
true
// Here, the result of validate is used in an if, so Swift is expecting
// a Bool result and calls the (String) -> Bool version of validate
if validate("this is a test") {
print("the string is valid")
} else {
print("the string is invalid")
}
the string is invalid
let v: Validity = validate("what about this one?")
switch v {
case .valid:
print("valid")
case .invalid(let reason):
print("invalid: \(reason)")
}
invalid: String too long

How to compare enum with associated values by ignoring its associated value in Swift?

Edit: As Etan points out, you can omit the (_) wildcard match to use this more cleanly:

let number = CardRank.Number(5)
if case .Number = number {
// Is a number
} else {
// Something else
}

Unfortunately, I don't believe that there's an easier way than your switch approach in Swift 1.2.

In Swift 2, however, you can use the new if-case pattern match:

let number = CardRank.Number(5)
if case .Number(_) = number {
// Is a number
} else {
// Something else
}

If you're looking to avoid verbosity, you might consider adding an isNumber computed property to your enum that implements your switch statement.

Comparing Java enum members: == or equals()?

Both are technically correct. If you look at the source code for .equals(), it simply defers to ==.

I use ==, however, as that will be null safe.

How to test equality of Swift enums with associated values

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.

how to use `if case` statement as boolean in swift

case .gem = cookie.type is not a boolean expression, and if statements not only accept boolean expressions. However, in the closure argument for filter, you must write an Bool expression or a block that returns Bool.

One way to do that is:

cookies.filter { 
if case $0.type = .gem { return true }
else { return false }
}

Or, you can add convenient properties to CookieType that gives you Bool values, if you tend to do this a lot:

enum CookieType {
case regular(type: Int)
case gem(type: GemType)

var isRegular: Bool {
if case .regular = self { return true }
else { return false }
}

var isGem: Bool {
if case .gem = self { return true }
else { return false }
}
}

Then you can do:

cookies.filter(\.isGem)

Test enum for equality in for ... where clause

You can use if case:

for status in statuses {
if case .failed = status {
...
}
}

But, unfortunately, you can't use case with the where clause of for loop.


In this case, though, because you've defined .failed to be equal to another regardless of what the error associated value was, you theoretically could do:

for status in statuses where status == .failed(error: "") {
show("\(status)")
}

I'm not crazy about that pattern because (a) it's contingent upon the fact that .failed values are equal even if they have different error associated values; and (b) it results in code that is easily misunderstood.

Using enum vs Boolean?

It totally depends on your requirement/specification. If you only want to record the status as active or inactive, the best way is to use boolean.

But if in the future, you will have a status such as,

  • ACTIVE
  • INACTIVE
  • SUSPENDED
  • BLOCKED

Enums is perfect for you. In your case, for now, a boolean is sufficient. Don't try overcomplicate things too early, you'll lose focus in your design & development of your system.



Related Topics



Leave a reply



Submit