Using Switch Statements for Type Checking in Swift

Originally posted 1/16/2016. Updated for Swift 3 on 12/18/16

Goal: Use switch statements to simplify and standardize type checking in Swift.

One thing that bothers many developers about using if statements for type checking in Objective-C is that it suggests a specific order of importance. For example, here's how you might use type checking in an Objective-C prepareForSegue method.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.destinationViewController isKindOfClass:[TKDetailViewController class]]) {
        //prepare TKDetailViewController
    } else if ([segue.destinationViewController isKindOfClass:[TKModalViewController class]]) {
        //prepare TKModalViewController
    }
}

The order of TKDetailViewController and TKModalViewController doesn't really matter here. The purpose of the code is just to check the type of the destination view controller, and the order in which it checks possible types is arbitrary. Here's an equivalent version in Swift:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.destination is DetailViewController {
        //prepare DetailViewController
    } else if segue.destination is ModalViewController {
        //prepare ModalViewController
    }
}

This Swift version is much more concise, but we still have the same problem. In Swift, however, we can now use a switch statement for type checking:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    switch segue.destination {
    case is DetailViewController:
        //prepare DetailViewController
    case is ModalViewController:
        //prepare ModalViewController
    default:
        break
    }
}

While it's unavoidable to list the types to be checked in a particular order, the intent of this code is clearer. Also notice that this version is even more concise since we don't have to repeat "if segue.destinationViewController is" each time.

Another situation where this is useful is in checking types that conform to a protocol. Here's an example using a version of the Drawable protocol from Apple's WWDC 2015 talk Protocol-Oriented Programming in Swift.

protocol Drawable {
    func draw()
}

struct Circle: Drawable { ... }
struct Polygon: Drawable { ... }
struct Diagram: Drawable { ... }

let circle = Circle()
let triangle = Polygon()
let pentagon = Polygon()
let diagram = Diagram()
let drawables: [Drawable] = [circle, triangle, pentagon, diagram]
for drawable in drawables {
    switch drawable {
    case is Circle:
        print("This is a circle.")
    case is Polygon:
        print("This is a polygon.")
    case is Diagram:
        print("This is a diagram.")
    default:
        break
    }
}

Again, using the switch statement makes it clearer that order doesn't matter here and also makes the code more concise. Note that we still need a default case because we're checking against all types, not just those that conform to Drawable.