292

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.

5 Answers 5

600

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")
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Hi, Rob. Just curiosity: since we are not using thing in switch` in any of the cases above, what would be the use here using thing? I failed to see it. Thanks.
thing is the value being tested in each case. So if thing is a Movie, it's value will be bound to the symbol movie.
60

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

2 Comments

putting two cases together just to separate them via if is probably not the best example to proof your point.
It would probably be better if value is something that can be one of Int, Float, Double, and treating Float and Double the same way.
54

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

Comments

45

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

1 Comment

I prefer as because it cast the type too.
0

A nice thing I've just discovered is that the switch syntax lets you "destructure" an enum with associated values in to constrained types.

Here's an example I've got in some code here that handles a Result

func showResult(_ result: Result<some Any, Error>) {
    let title: String
    let message: String

    switch result {
    case let .success(success as CustomDebugStringConvertible):
        /// The result has a custom debug description, so use that.
        title = "Success"
        message = "\(success.debugDescription)"

    case let .success(success):
        /// For general success, just use the description.
        title = "Success"
        message = String(describing: success)

    case let .failure(error):
        title = "Error"
        message = "\(error.reason ?? error.localizedDescription)"
    }

    show(title: title, message: message)
}

Short discussion of type casting…

In general I try to avoid the kind of type casting this question is asking for. I avoid it because the compiler doesn't do much to help us "get this right".

For example, we might start with our switch supporting a few cases in a special way, and we provide these cases, and everything works.

However, if at some future time we change the supported types, or we change our type's conformances, or we start sending along different types, then there will be no warning or error from the compiler that we've done something wrong. This alarms me – I want a build error when I've broken something, or someone else has. With this type casting approach there is no error.

For this reason, I usually prefer to have an enum with associated values for each explicit type I care about. Now the compiler can help me out.

However – as in this case, sometimes, it's just a whole lot easier to use a bit of type casting. I really try to save this approach for those few times that crop up.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.