Search code examples
swiftswitch-statementoptional-binding

How to use optional binding in switch statement in prepare(segue:)


In swift you can use a cool feature of the switch statement in prepare(segue:) to create cases based on the type of the destination view controller:

Example:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    switch segue.destination {

    case let detailViewController as DetailViewController:
      detailViewController.title = "DetailViewController"
    }
    case let otherViewController as OtherViewController:
      otherViewController.title = "OtherViewController"
    }
}

However, what if the segue is triggered by a split view controller, so the destination is a navigation controller, and what you really want to do is switch on the class of the navigation controller's top view controller?

I want to do something like this:

case let nav as UINavigationController,
     let detailViewController = nav.topViewController as? DetailViewController:
    //case code goes here

Where I have the same construct that I use in a multiple part if let optional binding.

That doesn't work. Instead, I have to do a rather painful construct like this:

case let nav as UINavigationController
  where nav.topViewController is DetailViewController:
  guard let detailViewController = nav.topViewController as? DetailViewController
    else {
      break
  }
  detailViewController.title = "DetailViewController"

That works, but it seems needlessly verbose, and obscures the intent. Is there a way to use a multi-part optional binding in a case of a switch statment like this in Swift 3?


Solution

  • I worked out a decent solution to this problem.

    It involves doing some setup before the switch statement, and then using a tuple in the switch statement. Here's what that looks like:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let dest = segue.destination
        let navTopVC = (dest as? UINavigationController)?.topViewController
        switch (dest, navTopVC) {
    
        case (_, let top as VC1):
            top.vc1Text = "Segue message for VC1"
    
        case (_, let top as VC2):
            top.vc2Text = "Segue message for VC2"
    
        case (let dest as VC3, nil):
            dest.vc3Text = "Segue message for VC3"
    
        default:
            break
        }
    }