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
How to Create a Hotspot Network in iOS App Using Swift
Why Use an Extra Let Statement Here
Type of Expression Is Ambiguous Without More Context in Xcode 11
Is There an Kotlin Equivalent 'With' Function in Swift
Swift Firestore Check If Documents Exists
Swift - Exit Outer Function from Closure
Swift- How to Display Image Over Button
Argument of '#Selector' Does Not Refer to an '@Objc' Method, Property or Initializer
Why Is Deinit Not Called Until Uiview Is Added to Parent Again
Does Swift Optimise Chained Creation and Copy of Structs
Swift:Pause and Resume Nstimer
How to Repeat Animation Forever in Swift (Huge_Valf)
Zoom to Fit Current Location and Annotation on Map
Ambigious Reference to Member Request() Issues with Alamofire After Migration to Swift 3
How to Use Case Enum Comparison as a Boolean Expression
How to Make Function That Some Parameters Not Required in When Call It in iOS Swift 3