Mischa
Mischa

Reputation: 17231

Make an enum with two cases convertible to Bool in Swift

I have an enum that I use for text validation defined as follows:

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

My TextValidator has a function that checks a text input and returns a Validity:

func validate(_ string: String) -> Validity

which is convenient because in case of an error I can display the validation failure reason to the user without much effort.

In many cases, however, I simply need a boolean check if the string is valid – for example for enabling / disabling a submit button. The code that I'm using for this right now is:

let validity = validator.validate("user input string")
submitButton.isEnabled = (validity == .valid)

which is kind of bad to read and thus, I would prefer to write something like:

let isValid: Bool = validator.validate("user input string")
submitButton.isEnabled = isValid

or shorter:

submitButton.isEnabled = validator.validate("user input string")

As a "quick fix" I have defined a computed boolean property isValid on my Validity enum so I can at least write:

submitButton.isEnabled = validator.validate("user input string").isValid

but it seems kind of verbose to me to append .isValid to a validation result and I would very much prefer to directly check the validity and treat it as a boolean when used in a context that expects a boolean via type inference.

So my question is:

Is there a way to make an enum implicitly convertible to Bool in Swift?

Or if not: Can you think of another approach that retains the advantages of both – the Bool and the enum type?

Upvotes: 2

Views: 2257

Answers (1)

vacawama
vacawama

Reputation: 154593

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

Upvotes: 2

Related Questions