Extract value from enum using guard or if case in Swift - swift

I have enum with cases:
case one(value: myClassOne)
case two(value: myClassTwo)
I want to check value of that enum. Now i ended with:
switch self.model! {
case .one:
// Great, my case here
default:
break
}
But i rather want to do something like:
if case self.model(let value) is myClassOne { // do smth } // Not compiling
Is there is easy way to simply extract enum value and check it for some condition or class equality?

This is the correct syntax:
if case .one(value: let value) = self.model {
// do something with `value`
}
guard case .one(value: let value) = self.model else {
//handle case where self.model != .one
}
// do something with `value`

Related

Accessing Associated Values in an Array of Swift Enum in an Opaque Fashion

I haven't really found what I need in the associated questions. That may be because I'm a bit thick, and didn't see it, but here's my quandary:
I have a computed property that returns an Array of enums with associated values.
The values are all the same types (in my use case, the type is a "flag"), but the enums are different.
I'd like to see if there was some way to cycle through the Array, looking at only the associated values, regardless of the enum type. I am attaching a small playground that shows what I mean:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
print(String(describing: each))
}
print();
// This works:
for each in a {
if case let .valueA(value) = each {
print(value)
}
if case let .valueB(value) = each {
print(value)
}
if case let .valueC(value) = each {
print(value)
}
}
print();
// So does this:
for each in a {
var value: Int = 0
switch each {
case .valueA(let val):
value = val
case .valueB(let val):
value = val
case .valueC(let val):
value = val
}
print(value)
}
// What I want:
//for each in a {
// if case let A(value) = each {
// print(value)
// }
//}
I want to be able to treat each member of the collection, and extract its flag, and then make a decision, based on that flag.
I know that I could do it with a big ol' switch statement, with all the enum values (second go), but it would be nice if there were some generic way to just access all the values.
You would still have to test against the each case, but you could do
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
switch each {
case .valueA(let val), .valueB(let val), .valueC(let val):
print(val)
}
}
This would still apply if you have cases with associated values of different types:
enum Cases {
case one(Int)
case two(Int)
case three(Int, String)
case four(String)
case five(String)
}
let testArray = [Cases.one(1), .two(2), .three(3, "three"),
.four("four"), .five("five")]
// Matching associated Ints
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal), .three(let intVal, _):
print(intVal)
case .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching associated Strings
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal):
print(intVal)
case .three(_, let strVal), .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching any type, cast to Any
for value in testArray {
switch value {
case .one(let anyVal as Any), .five(let anyVal as Any):
print(anyVal)
default:
continue // skipping these cases
}
}
The important takeaway is that you are binding the same var names in every statement whose value you are trying to match, meaning you can't match values that aren't available in ever statement:
switch ... {
// This won't work, because myInt is not defined in case .two
// and myOtherInt isn't defined in case .one
case .one(let myInt), .two(let myOtherInt):
...
}
To add to #Kiril's answer, if you wanted computed values for cases with different types of associated values, you can define optional computed vars to return those values:
extension Cases {
var intValue: Int? {
switch self {
case .one(let myInt), .two(let myInt), .three(let myInt, _):
return myInt
default:
return nil
}
}
var strValue: String? {
switch self {
case .three(_, let myStr), .four(let myStr), .five(let myStr):
return myStr
default:
return nil
}
}
}
I'd suggest a slight modification on your enum:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
var flag: Int {
switch self {
case .valueA(let flag),
.valueB(let flag),
.valueC(let flag):
return flag
}
}
}
That makes any loop that just needs a flag trivial:
for each in a {
print(each.flag)
}

Access enum associated value as optional

How can I access an enum value for a specific case without having to implement an enum function for each case?
I'm thinking something like this:
enum Result<S, F> {
case success(S)
case failure(F)
}
let resultSuccess = Result<Int, String>.success(1)
let resultFailure = Result<Int, String>.failure("Error")
let error: String? = case let .failure(error) = resultFailure
Something like this would also be acceptable:
let error: String? = (case let .failure(error) = resultFailure) ? error : ""
Obviously this doesn't even compile, but that's the gist of what I want.
Is that possible?
EDIT: explaining why my question is not the same as Extract associated value of enum case into a tuple
It's very simple, that answer access the values inside the if statement, rather than as an Optional like in my question. To specify a little more, imagine I have to extract values from multiple enums, and maybe enums inside enums, for that I'd need nested if's, or guard's, but with guard I wouldn't be able to continue the flow, since it forces a return.
Add two computed properties for success case and failure case respectively.
enum Result<S, F> {
case success(S)
case failure(F)
var successValue: S? {
switch self {
case .success(let value):
return value
case .failure:
return nil
}
}
var failureValue: F? {
switch self {
case .failure(let value):
return value
case .success:
return nil
}
}
}
let resultSuccess = Result<Int, String>.success(1)
let resultFailure = Result<Int, String>.failure("Error")
if let error = resultFailure.failureValue {
// do something
print(error)
}
I'm afraid this is the most you can get:
let error: String?
if case .failure(let e) = resultFailure {error = e}
This technique from the question "Accessing an Enumeration association value in Swift" will do it:
enum Result<S, F> {
case success(S)
case failure(F)
}
let resultFailure = Result<Int, String>.failure("Error")
var error: String?
if case let Result.failure(value) = resultFailure {
error = value
}
print(error) // Optional("Error")
This will also work in place of the "if case let =" construct:
switch resultFailure {
case let Result.failure(value): error = value
default : error = nil
}
Here you go, 1 line and uses let:
let error: String? = { if case let .failure(value) = resultFailure { return value }; return nil }()

How can I check an enum's value and its associated enum's value with a single case statement?

Please have a look at the switch statement below. I am looking for a more swifty way to perform the test; something like:
case let .b(other) where .x = other // This does not compile
Is it possible?
enum MyEnum {
case a
case b(MyOtherEnum)
}
enum MyOtherEnum {
case x
case y
}
func check(value: MyEnum) {
switch value {
case let .b(other):
if case .x = other {
print("Got it!")
}
default:
break
}
}
If you are only interested in the case MyEnum.b(.x) and no other
cases then you can also use if with a case pattern:
func check(value: MyEnum) {
if case .b(.x) = value {
print("Got it!")
}
}
func check(value: MyEnum) {
switch value {
case .b(.x):
print("Got it!")
case .b(_):
print("Not it!")
default:
break
}
}
let myVar = MyEnum.b(.x)
check(value: myVar)
// prints "Got it!"
You need to use == not =:
case let .b(other) where .x == other
This works fine for me:
func check(value: MyEnum) {
switch value {
case let .b(other) where other == .x:
print("bx")
case let .b(other) where other == .y:
print("by")
default:
break
}
}
check(value: MyEnum.b(.x)) // prints "bx"
check(value: MyEnum.b(.y)) // prints "by"

How to pattern match enum in a single line in Swift?

In Swift, if I have an enum:
enum MyEnum {
case foo(FooType)
case bar(BarType)
}
I can pattern match with either switch
switch enumValue {
case .foo(let fooValue):
// ... use fooValue
case .bar(let barValue):
// ...
}
...or case let
if case let .foo(fooValue) = enumValue {
// ... use fooValue
}
QUESTION: is it possible to match in one expression to check if it's of type .foo(FooType) to produce an optional FooType??
The equivalent multiline version is:
var x: FooType?
if case let .foo(fooValue) = enumValue {
x = fooValue
}
Something to the effect of
let .foo(x) = enumValue or nil
where fooValue is bound to a FooType? value or nil if it's not .foo.

Swift: testing against optional value in switch case

In Swift, how can I write a case in a switch statement that tests the value being switched against the contents of an optional, skipping over the case if the optional contains nil?
Here's how I imagine this might look:
let someValue = 5
let someOptional: Int? = nil
switch someValue {
case someOptional:
// someOptional is non-nil, and someValue equals the unwrapped contents of someOptional
default:
// either, someOptional is nil, or someOptional is non-nil but someValue does not equal the unwrapped contents of someOptional
}
If I just write it exactly like this, the compiler complains that someOptional is not unwrapped, but if I explicitly unwrap it by adding ! to the end, I of course get a runtime error any time someOptional contains nil. Adding ? instead of ! would make some sense to me (in the spirit of optional chaining, I suppose), but doesn't make the compiler error go away (i.e. doesn't actually unwrap the optional).
Optional is just a enum like this:
enum Optional<T> : Reflectable, NilLiteralConvertible {
case none
case some(T)
// ...
}
So you can match them as usual "Associated Values" matching patterns:
let someValue = 5
let someOptional: Int? = nil
switch someOptional {
case .some(someValue):
println("the value is \(someValue)")
case .some(let val):
println("the value is \(val)")
default:
println("nil")
}
If you want match from someValue, using guard expression:
switch someValue {
case let val where val == someOptional:
println(someValue)
default:
break
}
And for Swift > 2.0
switch someValue {
case let val where val == someOptional:
print("matched")
default:
print("didn't match; default")
}
As of Xcode 7, “a new x? pattern can be used to pattern match against optionals as a synonym for .some(x)”. This means that in Swift 2 and later the following variation of rintaro's answer will work as well:
let knownValue = 5
switch someOptional {
case knownValue?:
// Contents of someOptional are knownValue, defined above.
case let otherValue?:
// Contents of someOptional are *any* non-nil value not already tested for.
// Unwrapped contents are assigned to otherValue for use inside this case.
default:
// someOptional is nil.
}
In Swift 4 you can use Optional of Apple to wrappe optional
https://developer.apple.com/documentation/swift/optional
Example
enum MyEnum {
case normal
case cool
}
some
let myOptional: MyEnum? = MyEnum.normal
switch smyOptional {
case .some(.normal):
// Found .normal enum
break
case .none:
break
default:
break
}
none
let myOptional: MyEnum? = nil
switch smyOptional {
case .some(.normal):
break
case .none:
// Found nil
break
default:
break
}
default
let myOptional: MyEnum? = MyEnum.cool
switch smyOptional {
case .some(.normal):
break
case .none:
break
default:
// Found .Cool enum
break
}
Enum with value
enum MyEnum {
case normal(myValue: String)
case cool
}
some value
let myOptional: MyEnum? = MyEnum.normal("BlaBla")
switch smyOptional {
case .some(.normal(let myValue)) where myValue == "BlaBla":
// Here because where find in my myValue "BlaBla"
break
// Example for get value
case .some(.normal(let myValue)):
break
// Example for just know if is normal case enum
case .some(.normal):
break
case .none:
break
default:
break
}
You can explicitly mention all cases along with nil as an additional case to handle the optional:
switch optionalEnumValue {
case .caseOne:
break
case .caseTwo:
break
case .caseN:
break
case nil:
break
}