Use Guard to trap all enum states other than one - swift

Hitting a block on using a guard. Ideally want to do a guard statement that traps all enum states other than one, so would be something like:
guard case .notInUse != foundTransition.validToObject.isInSituation else {
fatalError("Transition: The toObject is already in a situation")
}
But this non matching test does not seem to be allowed. So instead using the below if statement:
if case .notInUse = foundTransition.validToObject.isInSituation {} else {
fatalError("Transition: The toObject is already in a situation")
}
It works but feels a guard would be neater. Any ideas?

It is impossible to negate a case statement.
You either need to use your if statement, or make the enumeration Equatable, in which case you would just drop the case keyword.
guard foundTransition.validToObject.isInSituation != .notInUse
Alternatively, you can use a guard statement that is backed by a switch or if. But you'll never get rid of them! 😈
guard ({
if case .notInUse = foundTransition.validToObject.isInSituation {
return false
}
return true
} ()) else {
fatalError("Transition: The toObject is already in a situation")
}

Related

Swift Pattern Matching in Catch for Enum with Associated Values

I would like to figure out how to pattern match against an enum-with-associated-value property of an error type in a catch. Everything works as expected with an enum without associated values, but I can't seem to figure out the correct pattern for this situation.
struct MyError: Error {
enum Size {
case big, small
}
enum Solution {
case runAway
case other(String)
}
let size: Size
let solution: Solution
}
func action() {
do {
// ...
}
catch let error as MyError where error.size == .big {
// This works fine, as `size` has no associated values.
}
catch let error as MyError where error.solution == .other {
// I want to handle all cases of `Solution.other` here, regardless of the associated value.
}
catch {
// ...
}
}
The second catch pattern won't compile (as expected due to the enum with associated value). The usual way I'd accomplish this would be a if case .runAway = error.solution {...}, but integrating this in the catch pattern is the problem.
I tried many combinations of if case/let case/case let, but couldn't get this working in a single catch pattern matching statement. This feels like it should be possible given the power and flexibility of pattern matching, so I'm hoping I've just overlooked something.
Thanks for the assistance!
This feels possible, but isn't :/. What you are trying to use is an enum case pattern. According to here, an enum case pattern is only allowed in switch, if, while, guard, and for statements.
You can add an isOther property in Solution:
var isOther: Bool {
if case .other = self {
return true
} else {
return false
}
}
And then use it in the catch:
do {
// ...
}
catch let error as MyError where error.size == .big {
// ...
}
catch let error as MyError where error.solution.isOther {
// ...
}
catch {
// ...
}
There are 2 things which should be fixed in your sample:
To compare cases of an enum it should be equitable, isn't it? For such a simple enum just mark Solution as Equitable.
Default case for a catch isn't handled, so you need to add it, eg:
do {
...
}
catch let error as MyError where error.size == .big {
// This works fine, assizehas no associated values.
}
catch let error as MyError where error.solution == .runAway {
// I want to accomplish this comparison.
}
catch let error {
...
}

How to access implict closure parameters from higher closure level

I want to iterate an enum and then use $0 in a switch/case statement down one level in a closure that is called in a fetch operation inside the enum iteration loop, as follows:
enum GenericType: CaseIterable {
case purchase
case sale
// etc....
}
Then the code to use is as follows:
GenericType.allCases.forEach {
// let type = $0
Manager.fetchItems(ofType: $0, onSuccess: { (data) in
switch $0 {
case purchase:
// Do something
case sale:
// Do something
}
}
Xcode 10 assumes that $0 refers to data (the parameter in the closure) and gives this error message:
Anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'data'?
I am able to make it work with before the fetch:
let type = $0
And then using type in the switch/case statement.
Is there a way to access the $0 shorthand argument from the higher level context inside a closure? Is the workaround a feasible solution?
Thx
$0 can only ever refer to the first closure context "up the chain". To access the parameters of outter closures, you need to name them:
GenericType.allCases.forEach { genericType in
Manager.fetchItems(ofType: genericType, onSuccess: { data in
switch genericType {
case .purchase: return
// Do something
case .sale: return
// Do something
}
})
}
Building on #Alexander and #Daniel's answer/inputs, this is the way I implemented the for loop without the switch-case statement.
enum GenericType: CaseIterable {
case purchase
case sale
var manager: GenericManager {
switch self {
case .purchase:
return PurchaseManager.shared
case .sale:
return SalesManager.shared
}
}
}
PurchaseManager and SaleManager are subclasses of GenericManager and they override the processFetchData(_:) method.
And the code is as follows:
GenericType.allCases.forEach { genericType in
FetchManager.fetchItems(ofType: genericType, onSuccess: { data in
genericType.manager.processFetchData(data)
})
}
Strictly speaking I only moved the switch statement from the biz logic to the enum declaration, but it makes it a bit more elegant.

Refer to the value of switch statement in a branch

Is there a way in Swift to refer to the value I am switching on in a branch? For example:
switch UIDevice.current.userInterfaceIdiom {
// cases
default:
fatalError("User interface idiom \(value) is not supported")
}
I am thinking about something similar to the implicit error reference inside catch block:
do {
// ...
} catch {
print(error) // 'error' is defined implicitly
}
Of course, I can create a variable myself, but I'd like to avoid that.
There is no built-in variable, but you can easily capture the value yourself with the case let pattern:
switch UIDevice.current.userInterfaceIdiom {
// cases
case let value:
fatalError("User interface idiom \(value) is not supported")
}
Note: This will match anything, so it replaces the default case and it should be the last case in your switch.
You can use a where clause to allow the capture of values other than everything:
switch 1 + 2 * 3 {
case let value where 0...9 ~= value:
print("The value \(value) is a single digit value")
case let value:
print("\(value) is not a single digit value.")
}
but it would be easier in that case just to assign the value to the variable before the switch.

How to do if pattern matching with multiple cases?

I'm searching for the syntax to do pattern matching with multiple cases in an if case statement.
The example would be this:
enum Gender {
case Male, Female, Transgender
}
let a = Gender.Male
Now I want to check, if a is .Male OR .Female. But I would like to avoid using switch for this. However the switch statement would be like this:
switch a {
case .Male, .Female:
// do something
}
Is it possible to write this with if case?
I would expect this, but it didn't work :(
if case .Male, .Female = a {
}
A simple array does the trick:
if [.Male, .Female].contains(a) {
print("Male or female")
} else {
print("Transgender")
}
I'm simply amazed at Swift's ability to infer type. Here, it gets that .Male and .Female are of type gender from a.
If you have an associated value, you can simply create a Bool variable like this
extension Gender {
var isMaleOrFemale: Bool {
switch self {
case .Male, .Female:
return true
default:
return false
}
}
And usage:
if a.isMaleOrFemale {
// Your code here
}
You should use a collection. In JavaScript I would write something like this:
if ([Gender.Male, Gender.Female].includes(actualGender))
console.log(actualGender);
Note that I have not a clue about swift, or how to do the same in that language, so here is a relevant answer in the topic: https://stackoverflow.com/a/25391725/607033 :D
EDIT: This is the Swift version:
if [.Male, .Female].contains(a) {
}
For pattern matching, what you describe will not work yet. You could do this in your case. But if it cannot be convert into a hashValue. Then this would not work either.
// Using Pattern Matching for more than one case.
if case 0...2 = a.hashValue {
print("Hello")
}
//Normal if else
if a == .Male || a == .Female {
print("Hello")
}

Swift 2 value extraction from Enum

Prior to Swift 2, I would often use enums with associated values and add functions to extract specific values, like so:
public enum Maybe <T> {
case Unknown
case Known(T)
public var value: T? {
switch self {
case .Unknown: return nil
case .Known(let value): return value
}
}
}
This would allow me to do something like this:
let maybe = .Known("Value")
let val = maybe.value ?? "Another Value"
I would like to get rid of these convenience functions and rely on Swift 2's new syntax. This is possible doing something like:
let val: String
if case .Known(let value) = maybe {
val = value
} else {
val = "Another Value"
}
But I can't figure out how to condense this back into a single line using the ?? operator or even ternary operator.
Is this even possible or am I stuck with defining "extraction" optionals on the enum?
Update (clarification)
The Maybe enum is just an example, but the solution would need to work on Enums that have multiple associated values... like an Either:
public enum Either<L, R> {
case Left(Box<L>)
case Right(Box<R>)
public func left() -> L?{
switch self {
case let Left(value):
return value.value
default:
return nil
}
}
public func right() -> R?{
switch self {
case let Right(value):
return value.value
default:
return nil
}
}
}
The syntax I'm looking for would be something like:
let val = (case .Known(let value) = maybe) ?? "AnotherValue"
What I want to do is easily extract an associated value for a specific case, else provide a default.
For Either it might be something like:
let val = (case .Left(let value) = either) ?? "AnotherValue"
Make sense?
The syntax you want isn't possible in Swift today (and feels unlikely for Swift tomorrow, but I often am surprised). The best available solutions are extraction functions like left() -> L? and right() -> R?. case is not a generic value-returning function that you can extend. See Rob Rix's Either for some of the best current thinking on this problem.
A key choice in Swift is that there are many statements that are not expressions. switch is one of them. Until Swift makes things like switch and if be expressions, it will be very hard to build this kind of syntax IMO.
Just define ?? for it:
func ??<T>(lhs: Maybe<T>, #autoclosure defaultValue: () throws -> T) rethrows -> T {
switch lhs {
case .Unknown: return try defaultValue()
case .Known(let value): return value
}
}
let maybe = Maybe.Known("Value")
let val = maybe ?? "Another Value"
Doing it this way gives us some nice features in Swift2. For instance, we can lazily evaluate the rhs, and we can handle throwing in the rhs:
func computeIt() -> String {
print("LAZY!")
return "Computed"
}
maybe ?? computeIt() // Does not print "LAZY!"
Maybe.Unknown ?? computeIt() // Does print "LAZY!"
enum Error: ErrorType {
case Failure
}
func fail() throws -> String {
throw Error.Failure
}
try maybe ?? fail() // Doesn't throw
do {
try Maybe.Unknown ?? fail() // throws
} catch {
print("THROW")
}