Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 783
Duncan C Avatar asked Oct 26 '25 04:10

Duncan C


2 Answers

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
    }
}
like image 187
Duncan C Avatar answered Oct 29 '25 06:10

Duncan C


You might find this extension useful…

extension UIStoryboardSegue {
    var destinationNavTopViewController: UIViewController? {
        return (destination as? UINavigationController)?.topViewController ?? destination
    }
}

Then you can simply…

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    switch segue.destinationNavTopViewController {
    case let detailViewController as? DetailViewController:
        // case code goes here
}

Note that the ?? destination makes sure the return value is non-optional, and also allows it to work in places where the destination could also be a non-navigation controller.

like image 39
Ashley Mills Avatar answered Oct 29 '25 06:10

Ashley Mills



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!