How to make switch on Type - swift

I would like to make a switch statement on Type of given protocol. Lets say I have:
protocol A {}
struct SA: A {}
struct SB: A {}
let aself: A.Type = SA.self
let bself: A.Type = SB.self
switch aself {
case is SA:
break
default:
break
}
Can I somehow make such switch work? This gives me warning that such conversion always fails. Can this be done somehow?

You can write something like this:
switch aself {
case is SA.Type:
print("is SA.Type")
default:
print("Unknown type")
}
Or this:
switch ObjectIdentifier(aself) {
case ObjectIdentifier(SA.self):
print("is SA.Type")
default:
print("Unknown type")
}
(Some code explaining different behavior for subclasses as I commented below.)
class CA: A {}
class CAA: CA {}
let caaself: A.Type = CAA.self
switch caaself {
case is CA.Type:
print("is CA.Type") //->is CA.Type
default:
print("Unknown type")
}
switch ObjectIdentifier(caaself) {
case ObjectIdentifier(CA.self):
print("is CA.Type")
default:
print("Unknown type") //->Unknown type
}
You may need to use ObjectIdentifier when you want to exclude subclasses in your type matching. (There may be other ways, but I do not think of any, now.)

I assume that you'll also want to use the casted type value, in this situation you can use some pattern matching:
switch aself {
case let a as SA.Type:
break
default:
break
}
If you don't want to use the casted value:
switch aself {
case let _ as SA.Type:
break
default:
break
}

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)
}

How to check if enum does not match a pattern?

Note that I have read this post but that post uses a switch statement and it is supposed to do something (return true) when the pattern matches. I, on the other hand, don't want to do anything if the pattern matches and use an if-case statement.
I have this enum:
enum MyEnum {
case a
case b(Int)
case c
case d
}
Here's an instance of it:
let myEnum: MyEnum = .a
Now I want to do something if myEnum is not .b. Since .b has an associated value, I can't simply use an if statement check:
if myEnum != .b { // compiler error
// do my thing here
}
So I must use an if-case statement to pattern match it:
if case .b(_) = myEnum {
} else {
// do my thing here
}
But I really hate the use of the empty if clause. That just looks unswifty to me. I tried to naïvely do this:
if case .b(_) != myEnum { // compiler error!
// do my thing here
}
Is there a better way to do this other than using an empty if clause?
I still have code that should run regardless of whether the pattern matches, so a guard statement won't work.
This is purely minimal semantic change of your own code, but note that you can simply "discard" the empty if clause inline with the case pattern matching:
if case .b(_) = myEnum {} else {
// do your thing here
}
or, leaving out the redundant pattern matching for the associated value of case b:
if case .b = myEnum {} else {
// do your thing here
}
This looks somewhat like a guard clause, but without exiting the scope.
You could use a guard:
guard case .b = myEnum else {
// do your stuff here
return
}
The downside is that you have to exit the scope...
What about:
switch myEnum {
case .b(_):
break
default:
// do your thing here
}
Create a var on the enum which calculates if your value is not .b(_):
enum MyEnum {
case a
case b(Int)
case c
case d
var notB: Bool {
switch self {
case .b(_):
return false
default:
return true
}
}
}
MyEnum.a.notB // true
MyEnum.b(1).notB // false
MyEnum.c // true
MyEnum.d // true
Not the greatest answer since there is still a lot of code to do the check, but at least the check is only one line when you actually use it.
You can write a computed property and have it return a bool value depending on case
enum MyEnum {
case a
case b(Int)
case c
var isCaseB: Bool {
switch self {
case .b(_):
return true
default:
return false
}
}
}
then in your code cleanly check:
if !enumVal.isCaseB {
}
I checked the answer you mentioned in your question but I wasn't sure if you meant you didn't want to use a switch statement at all or just that you didn't want to mix it in with your other code. I think this is a nice and clean way to check prior to writing whatever implementation you need to do depending on the case.

How can I add raw values for an enum that doesn't have it?

Given an external enum that I can't change:
enum MyEnum {
case first
case second
}
How would I best make this RawRepresentable, or at least convertible to an Int (or String) ?
I could write an extension to mimic rawValue, but this feels rather clumsy:
extension MyEnum {
enum EnumError: Error {
case invalidValue
}
init (rawValue: Int) throws {
switch rawValue {
case 0:
self = .first
case 1:
self = .second
default:
throw EnumError.invalidValue
}
}
var rawValue: Int {
switch self {
case .first:
return 0
case .second:
return 1
}
}
}
What is a better way?
This works:
enum MyEnum {
case first
case second
}
extension MyEnum {
enum MyExtendedEnum:Int {
case first
case second
}
}
Its a bit more cleaner code anyways, and your call is now:
let myVar = MyEnum.MyExtendedEnum.RawValue()

How to check if an instance is of type enum

'emun' seems to me like a keyword or a primitive type.
And obviously following code does not compile:
if self is enum {
}
But how can I be able to check if certain protocol is implemented by any enum?
protocol Enumatable {
}
extension Enumatable {
func isEnum() -> Bool {
return self is enum //it does not compile
}
}
But what I really want is to set some kind of constraint in the protocol to force the adopting class to be an enum. Is that possible?
Thanks!
I'm not sure how performant it is to use Mirrors. But here you go:
enum SomeEnum {
case one
case two
}
let mirror = Mirror(reflecting: SomeEnum.one)
if let displayStyle = mirror.displayStyle {
switch displayStyle {
case .enum:
print("I am an enum")
default:
print("not an enum")
}
}

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
}