Can I use a cast in a case pattern? - swift

I have a switch case statement over an enum type with associated values in Swift:
enum Foo {
case Something(let s: Any)
// …
}
I would like to use a cast in the pattern match, a bit like this:
let foo: Foo = // …
switch foo {
case .Something(let a as? SpecificType):
// …
}
In other words, I would like the case pattern to succeed only if the cast succeeds. Is that possible?

Your example basically works as is:
enum Foo {
case Something(s: Any)
}
let foo = Foo.Something(s: "Success")
switch foo {
case .Something(let a as String):
print(a)
default:
print("Fail")
}
If you replace "Success" with e.g. the number 1 it will print "Fail" instead. Is that what you want?

You can use where clause:
enum Foo {
case Something(s: Any)
}
let foo: Foo = Foo.Something(s: "test")
switch foo {
case .Something(let a) where a is String:
print("Success")
default:
print("Failed")
}

Related

Enum with associated value as associated value of another enum (nested associated value)

I have enum Foo with associated value. Enum Foo serves as an associated value of another enum, Car. I would like to know how to access the 'nested' associated value of Foo, as shown in the example below:
enum Foo: Equatable {
case moo
case zoo(String)
}
enum Car {
case mar
case dar(Foo)
case gar(Foo)
}
func request(with type: Car) -> String {
switch type {
case .mar:
return "mar"
case .dar(let foo) where foo == .moo:
return "darmoo"
case .dar(let foo) where foo == .zoo(let value):
// how can I access the associated value of foo?
// This where syntax results in an error - Consecutive statements on a line must be separated by ';'
}
}
You can use pattern matching:
switch type {
case .mar:
return "mar"
case .dar(let foo) where foo == .moo:
return "darmoo"
case .dar(.moo):
return "dar: moo"
case let .dar(.zoo(value)):
return "dar: \(value)"
case .gar:
return "gar"
}
Or, if you want to handle Foo in the same manner for dar and gar, you can bind foo in one case:
switch type {
case .mar:
return "mar"
case .dar(let foo) where foo == .moo:
return "darmoo"
case let .dar(foo), let .gar(foo):
switch foo {
case .moo:
return "moo"
case let .zoo(value):
return value
}
}
As you rightly say, it's nested. So nest! Use a switch within the switch. This compiles:
func request(with type: Car) -> String {
switch type {
case .mar:
return "mar"
case .dar(let foo):
switch foo {
case .moo: return "darmoo"
case .zoo(let s): return s
}
case .gar:
return "gar"
}
}

Swift if case with logical OR

Say we have an enum:
enum MyEnum {
case foo(Int)
case bar
}
and we can do something like this:
func myFunc(_ foo: MyEnum, _ bar: MyEnum) {
if case .foo(_) = foo, case .bar = bar {...}
}
but what if I need some like this
if case .foo(_) = foo, case .bar = bar OR someVar == true {...}
where I want either case .foo(_) = foo, case .bar = bar to be true, or someVar to be true.
Apparently I can't put || there, and I couldn't figure out an alternative. Am I missing something simple?
I'm not sure if this is possible with a single if statement. However, you could use a switch statement like this:
enum MyEnum {
case foo(Int)
case bar
}
func myFunc(_ foo: MyEnum, _ bar: MyEnum, _ someVar: Bool) {
switch (foo, bar, someVar) {
case (.foo, .bar, _), (_, _, true):
break
default:
break
}
}

How i can compare to which nesting enum associated my value

this is my code
public enum Key {
enum A: String {
case x
}
enum B: String {
case y
}
enum C: String {
case z
}
}
Now I need to compare to which nesting enum the argument belongs,
it is possible something like this?
func readString(key: Key) {
switch key {
case .A:
//do smth
default:
break
}
}
I have found the solution to specifically my problem:
enum Key {
case a(A)
case b(B)
case c(C)
enum A: String {
case x
}
enum B: String {
case y
}
enum C: String {
case z
}
}
After this we can use switch-case.
But it will be better to do in the following way:
Creating protocol
Subscribe
Call this in generic
Something like this
protocol CKey: RawRepresentable where RawValue == String {
var nestingEnum: String { get }
}
enum Key {
enum A: String, CKey {
case x
var nestingEnum: String {
return "X"
}
}
enum B: String, CKey {
case y
var nestingEnum: String {
return "Y"
}
}
enum C: String, CKey {
case z
var nestingEnum: String {
return "Z"
}
}
}
And call this
func read<Item: CKey>(for key: Item) -> String {
//now we can read key.nestingEnum
}

How to define a `StrContains.unapply` for pattern match?

There is a method in my code, which need to check if the passing string contain some specified chars, and then do something.
The code is looking like:
def check(str: String) = {
if(str.contains("A")) {
doSomething()
} else if(str.contains("B")) {
doSomething()
} else if(str.contains("C")) {
doSomething()
} else {
doSomething()
}
}
I want to try pattern match on it, but not very satisfied with:
def check(str: String) = str match {
case s if s.contains("A") => doSomething()
case s if s.contains("B") => doSomething()
case s if s.contains("C") => doSomething()
case _ => doSomething()
}
I hope I can define a StrContains.unapply to use it like this:
def check(str: String) = str match {
case StrContains("A") => doSomething()
case StrContains("B") => doSomething()
case StrContains("C") => doSomething()
case _ => doSomething()
}
But now sure how to do it. Any thoughts?
The problem is that when you do case StrContains("A"), the compiler will first call StrContains.unapply/unapplySeq (whichever way it is defined) and only then try to match the returned result against "A". The "A" literal itself will never be passed to StrContains.unapply/unapplySeq, so it has no way to perform the call s.contains("A") inside unapply/unapplySeq.
Simply put, this means you'd need to define distinct objects such as StrContainsA/StrContainsB/StrContainsC which is clearly a worse situation than simply doing case s if s.contains("A").
However, there is an alternative (and somewhat contrived) solution that allows to define a single extractor while still being able to specify inline the substring to match for, and that is to exploit the fact that scala supports defining extractors based on string interpolation:
implicit class ContainsContext (val sc : StringContext) {
object StrContains {
def unapply(str: String): Boolean = {
val substr: String = sc.parts.mkString
str.contains(substr)
}
}
}
Usage:
def check(str: String) = str match {
case StrContains"A" => doSomething()
case StrContains"B" => doSomething()
case StrContains"C" => doSomething()
case _ => doSomething()
}

Swift: Test class type in switch statement

In Swift you can check the class type of an object using 'is'. How can I incorporate this into a 'switch' block?
I think it's not possible, so I'm wondering what is the best way around this.
You absolutely can use is in a switch block. See "Type Casting for Any and AnyObject" in the Swift Programming Language (though it's not limited to Any of course). They have an extensive example:
for thing in things {
switch thing {
case 0 as Int:
println("zero as an Int")
case 0 as Double:
println("zero as a Double")
case let someInt as Int:
println("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
println("a positive double value of \(someDouble)")
// here it comes:
case is Double:
println("some other double value that I don't want to print")
case let someString as String:
println("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
println("an (x, y) point at \(x), \(y)")
case let movie as Movie:
println("a movie called '\(movie.name)', dir. \(movie.director)")
default:
println("something else")
}
}
Putting up the example for "case is - case is Int, is String:" operation, where multiple cases can be used clubbed together to perform the same activity for Similar Object types. Here "," separating the types in case is operating like a OR operator.
switch value{
case is Int, is String:
if value is Int{
print("Integer::\(value)")
}else{
print("String::\(value)")
}
default:
print("\(value)")
}
Demo Link
In case you don't have a value, just any object:
swift 4
func test(_ val:Any) {
switch val {
case is NSString:
print("it is NSString")
case is String:
print("it is a String")
case is Int:
print("it is int")
default:
print(val)
}
}
let str: NSString = "some nsstring value"
let i:Int=1
test(str)
// it is NSString
test(i)
// it is int
I like this syntax:
switch thing {
case _ as Int: print("thing is Int")
case _ as Double: print("thing is Double")
}
since it gives you the possibility to extend the functionality fast, like this:
switch thing {
case let myInt as Int: print("\(myInt) is Int")
case _ as Double: print("thing is Double")
}