Swift 3 enum with associated value AND function comparison - swift

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?

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.

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
}

Related

Swift Use of unresolved operator '=~'

I'm trying to find a code for swift password strength checker, then I see this from github I fix some codes but there's a operator that I don't understand, it's =~
This is my code
enum PasswordStrength: Int {
case None
case Weak
case Moderate
case Strong
static func checkStrength(password: String) -> PasswordStrength {
var len: Int = password.count
var strength: Int = 0
switch len {
case 0:
return .None
case 1...4:
strength += 1
case 5...8:
strength += 2
default:
strength += 3
}
// Upper case, Lower case, Number & Symbols
let patterns = ["^(?=.*[A-Z]).*$", "^(?=.*[a-z]).*$", "^(?=.*[0-9]).*$", "^(?=.*[!##%&-_=:;\"'<>,`~\\*\\?\\+\\[\\]\\(\\)\\{\\}\\^\\$\\|\\\\\\.\\/]).*$"]
for pattern in patterns {
if (password =~ pattern).boolValue {
strength += 1
}
}
switch strength {
case 0:
return .None
case 1...3:
return .Weak
case 4...6:
return .Moderate
default:
return .Strong
}
}
}
and my error Use of unresolved operator '=~' was pointing at =~, what's the meaning of it and how can I resolve it?
=~ seems like a custom operator (or maybe the pattern matching operator ~= was different in previous Swift version, but I doubt that, since the pattern matching operator cannot handle regex matching or at least cannot handle it in Swift 5).
You can replace the =~ with if password.range(of: pattern, options: .regularExpression) != nil to check if a regular expression matches your password.
Also, enum cases should be lowerCamelCase
Full working code:
enum PasswordStrength: Int {
case none
case weak
case moderate
case strong
static func checkStrength(password: String) -> PasswordStrength {
let len = password.count
var strength: Int = 0
switch len {
case 0:
return .none
case 1...4:
strength += 1
case 5...8:
strength += 2
default:
strength += 3
}
// Upper case, Lower case, Number & Symbols
let patterns = ["^(?=.*[A-Z]).*$", "^(?=.*[a-z]).*$", "^(?=.*[0-9]).*$", "^(?=.*[!##%&-_=:;\"'<>,`~\\*\\?\\+\\[\\]\\(\\)\\{\\}\\^\\$\\|\\\\\\.\\/]).*$"]
for pattern in patterns {
if password.range(of: pattern, options: .regularExpression) != nil {
strength += 1
}
}
switch strength {
case 0:
return .none
case 1...3:
return .weak
case 4...6:
return .moderate
default:
return .strong
}
}
}
You can also simplify the for loop by using a where clause like this:
for pattern in patterns where password.range(of: pattern, options: .regularExpression) != nil {
strength += 1
}

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

Say you need to determine the actual associated type of an enum.
So, a situation like
enum MessageItem: Decodable {
case a(Images)
case b(Text)
case c(Reply)
...
}
I used to have code like this
xSome = x.filter {
switch $0 {
case .a(_):
return false
case .b(_):
return true
case .c(_):
return true
}
}
But then it was possible to have code like this
xSome = x.filter {
if case .a = $0 { return false }
return true
}
Is there now some way it Swift to compare against associated type producing a boolean?
So, something like:
xSome = x.filter {
return (case .a = $0)
}
So, something like anEnum.is( .someCase )
Is anything like this now in Swift?
(Naturally, I mean without adding a var in the enum, which of course you can do.)
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
}
}()

What's the best way to handle invalid arguments in scala

I'm writing a function that takes a request object, there are several ways that the request could be configured, but only one valid form, so before I perform my function I'd like to test how the object is configured. If I were doing this in java, I'd write something like this:
static void func(Request request) {
if (condition1)
return false
if (condition 2)
return false
if (condition 3)
return false
request.process()
return true
}
In scala though, in trying to avoid using return, I usually end up with this:
static void func(Request request) {
if (!condition1) {
if (!condition 2) {
if (!condition 3) {
request.process()
return true
}
else
return false
}
else
return false
}
else
return false
}
This is a simplified form as usually there are things that I need to do after condition 1, and then after condition 2, which mean that I can't just combine the conditions into a single if statement (e.g. condition 1 checks that something isn't null, then condition 2 checks if the value of that thing is valid).
What I'm wondering is how do I make my code clean and readable whilst still having these checks in place?
you can use pattern matching
request match {
case _ if (condition1 || condition2 || condition3) => false
case _ => request.process(); true
}
In Scala it is usual to return Option[value] rather than returning true/false and relying on side effects. And a Request object would normally be pure data, with processing done by an external function.
So a typical Scala function might look like this:
def func(req: Request): Option[Result] =
req match {
case Request(a, _, _) if condition1(a) => None
case Request(_, b, _) if condition2(b) => None
case _ =>
Some(processRequest(req))
}
You can just do a simple if else simplifying your conditions.
def func(request: Request) {
if (condition1 || condition2 || condition3)
false
else {
request.process()
true
}
}

Swift 2 - Use case for using break on if statement?

Swift 2's guide mentions that you can end program execution of an if statement. I personally have never used break with if-statement.
A break statement ends program execution of a loop, an if statement,
or a switch statement...When a break statement is followed by the
name of a statement label, it ends program execution of the loop, if
statement, or switch statement named by that label.
In what situation would one be using break in an if-statement? This language feature seems useless.
TEST:
if (true) {
break TEST
}
For example if you want to describe a number (with Strings) with reference to sets of numbers (even/rational/negative numbers) your code could look something like this:
if condition1 {
// code
if condition2 {
// code
if condition3 {
// code
if condition4 {
//code
}
}
}
}
You can achieve the same logic but without the nested ifs by refactoring it (using guard):
OuterIf: if condition1 {
// code
guard condition2 else { break OuterIf }
// code
guard condition3 else { break OuterIf }
// code
guard condition4 else { break OuterIf }
// code
}
// reads even better when breaking out of "do"
scope: do {
guard condition1 else { break scope }
// code
guard condition2 else { break scope }
// code
guard condition3 else { break scope }
// code
guard condition4 else { break scope }
// code
}
You might think that this can also be achieved with switch and fallthrough but this doesn't work with "normal" cases because it checks all conditions and if one condition is met all following conditions aren't even evaluated.
So the fallthough has to be called conditionally.
This does work but I isn't very readable not to mention its "beauty":
let x = 4
switch x {
case _ where condition1:
// code
if condition2 { fallthrough }
case _ where false:
// code
if condition3 { fallthrough }
case _ where false:
// code
if condition4 { fallthrough }
case _ where false:
// code
break
default: break
}
Using break with an if statement seems a bit contrived, and I can't think of a place where style would demand it. It does, however, save an extra level of indentation when skipping the latter portion of an if statement in an if-else clause, which can be useful for deeply nested loops.
In other languages, a popular (and/or controversial) idiom is to use labels for handling errors in deeply nested functions. For example, one might want to break out of a loop on error, like this:
func testBreak3() {
// doesn't compile!!!
let a = false, b = true, x = 10, y = 20, err = true
if !a {
if b && x > 0 {
if y < 100 {
if err {
break handleError
}
// some statements
} else {
// other stuff
}
}
}
return // avoid error handling
handleError:
print("error")
// handle the error
}
But in Swift (I'm using 2.0 as a reference), labels are different than with other languages; the above example doesn't compile for two reasons: The label isn't declared yet when it's used, and the label must be directly associated with a do, while, if, or case statement. Furthermore, break within an if or do statements requires that statement to be labeled. We can fix this as follows, although the changes make the solution less attractive due to additional tracking via the errorFlagged variable, making refactoring more attractive:
func testBreak() {
let a = false, b = true, x = 10, y = 20, err = true
var errorFlagged = false
nestedIf: if !a {
if b && x > 0 {
if y < 100 {
if err {
errorFlagged = true
break nestedIf
}
// some statements
} else {
// other stuff
}
}
}
// skip handling if no error flagged.
if errorFlagged {
print("error")
// handle error
}
}
I know this is old topic, but just now I used break and it was needed.
So my example
I have array of objects.
When user taps on a cell, i.parameter becomes True for the object in that cell.
I need to know when all the objects in the array have i.parameter = True , that's the condition to stop the game.
func forTimer(){
for i in array {
if i.parameter == false {
break
}
}
}
timer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(forTimer), userInfo: nil, repeats: true)
Even if one i.parameter = false, I do not need to check the rest of the array.
This function is called every millisecond, so I will not have to check the whole array every millisecond.

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

After reading How to test equality of Swift enums with associated values, I implemented the following enum:
enum CardRank {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
func ==(a: CardRank, b: CardRank) -> Bool {
switch (a, b) {
case (.Number(let a), .Number(let b)) where a == b: return true
case (.Jack, .Jack): return true
case (.Queen, .Queen): return true
case (.King, .King): return true
case (.Ace, .Ace): return true
default: return false
}
}
The following code works:
let card: CardRank = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack!")
} else if card == CardRank.Number(2) {
print("A two cannot be played at this time.")
}
However, this doesn't compile:
let number = CardRank.Number(5)
if number == CardRank.Number {
print("You must play a face card!")
}
... and it gives the following error message:
Binary operator '==' cannot be applied to operands of type 'CardRank' and '(Int) -> CardRank'
I'm assuming this is because it's expecting a full type and CardRank.Number does not specify an entire type, whereas CardRank.Number(2) did. However, in this case, I want it to match any number; not just a specific one.
Obviously I can use a switch statement, but the whole point of implementing the == operator was to avoid this verbose solution:
switch number {
case .Number:
print("You must play a face card!")
default:
break
}
Is there any way to compare an enum with associated values while ignoring its associated value?
Note: I realize that I could change the case in the == method to case (.Number, .Number): return true, but, although it would return true correctly, my comparison would still look like its being compared to a specific number (number == CardRank.Number(2); where 2 is a dummy value) rather than any number (number == CardRank.Number).
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.
Unfortunately in Swift 1.x there isn't another way so you have to use switch which isn't as elegant as Swift 2's version where you can use if case:
if case .Number = number {
//ignore the value
}
if case .Number(let x) = number {
//without ignoring
}
In Swift 4.2 Equatable will be synthesized if all your associated values conform to Equatable. All you need to do is add Equatable.
enum CardRank: Equatable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
https://developer.apple.com/documentation/swift/equatable?changes=_3
What I usually do to compare if two enum cases "match" no matter their associated value is:
I have a protocol Matchable:
protocol Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool
}
Then I make enums conform to it:
extension CardRank: Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case
(.number, .number),
(.jack, .jack),
(.queen, .queen),
(.king, .king),
(.ace, .ace):
return true
default:
return false
}
}
}
let card1: CardRank = .number(1)
let card2: CardRank = .number(2)
let card3: CardRank = .jack
print(card1 ~= card2) // true
print(card1 ~= card3) // false
Here's a simpler approach:
enum CardRank {
case Two
case Three
case Four
case Five
case Six
case Seven
case Eight
case Nine
case Ten
case Jack
case Queen
case King
case Ace
var isFaceCard: Bool {
return (self == Jack) || (self == Queen) || (self == King)
}
}
There's no need to overload the == operator, and checking for card type does not require confusing syntax:
let card = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack")
} else if !card.isFaceCard {
print("You must play a face card!")
}
I didn't want to conform Equatable (it didn't help me either) and I wanted to filter for other cases than a specific one, so instead of simply writing card != .Number I had to write the following. (I adjusted my code to this question.)
enum CardRank {
...
var isNumber: Bool {
if case .Number = self { return true }
return false
}
}
So I can write not a number in a complex condition:
if something && !card.isNumber { ... }
I wish I could just write card != .Number, but the compiler was always complaining with Type of expression is ambiguous without more context. Maybe in an upcoming swift version!
You don't need func == or Equatable. Just use an enumeration case pattern.
let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }
extension CardRank {
func isSameCaseAs(_ other: CardRank) -> Bool {
switch (self, other) {
case (.Number, .Number),
(.Jack, .Jack),
(.Queen, .Queen),
(.King, .King),
(.Ace, .Ace):
return true
default:
return false
}
}
}
let number = CardRank.Number(1)
let otherNumber = CardRank.Number(2)
number.isSameCaseAs(otherNumber) // true
Just create an extension and ignore the associated types.
From Swift 5.3, you can use the Comparable Enums feature:
enum CardRank: Comparable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
let cards: [CardRank] = [
.Queen, .Number(8), .Ace, .Number(3), .King
]
print(cards.sorted())
// [.Number(3), .Number(8), .Queen, .King, .Ace]