Elise
Elise

Reputation: 5144

Swift 3 enum with associated value AND function comparison

I have this struct that has an enum property as well as a function:

struct UserInput {
  enum State {
    case unrestricted
    case restricted(because: WarningType)

    enum WarningType {
      case offline
      case forbidden
    }
  }

  var config: UserInputConfig?
  var state: State = .unrestricted

  func isConfigured() -> Bool {
    // Arbitrary checks about the config...
  }
}

Is there a way to rewrite the following conditionals so that the check for isConfigured() and state are in the same statement?

if case .restricted = userInput.state {
  return 1
} else if userInput.isConfigured() {
  return 1
} else {
  return 0
}

It seems because the State enum uses associated values, you cannot simply write if userInput.state == .restricted || userInput.isConfigured(), you need to use the if case syntax. There must be a way around this?

Upvotes: 1

Views: 315

Answers (2)

vacawama
vacawama

Reputation: 154691

You would like to do this:

if case .restricted = userInput.state || userInput.isConfigured() {
    return 1
} else {
    return 0
}

but there is currently no way to do an OR with pattern matching. There are a couple of ways of doing AND.

By using DeMorgan's Laws, you can turn if a || b into if !(!a && !b) and by reversing the then and else clauses of your if statement, you can just check for if !a && !b.

Unfortunately, you can't say if !(case .restricted = userInput.state), but since your enum has only 2 cases, you can replace that with if case .unrestricted = userInput.state.

Now, how do you use that with another statement? You can't use && for the same reason you can't use ||.

You can check for the failing case by using a pattern that matches both failing conditions (which is using AND) and then return 1 if both failing conditions aren't met:

if case (.unrestricted, false) = (userInput.state, userInput.isConfigured()) {
    return 0
} else {
    return 1
}

Equivalently you can use a multi-clause condition:

if case .unrestricted = userInput.state, !userInput.isConfigured() {
    return 0
} else {
    return 1
}

In addition to being shorter and IMO easier to read, this second method can short circuit and skip calling userInput.isConfigured in the case where case .unrestricted = userInput.state fails.

Upvotes: 2

XmasRights
XmasRights

Reputation: 1509

You can do it really cleanly with a switch statement, and pattern matching:

switch userInput.state
{
   case .unrestricted:
        return userInput.isConfigured() ? 1 : 0;

   case .restricted(_):
        return 1
}

Upvotes: 1

Related Questions