Skip to content

Instantly share code, notes, and snippets.

@msewell
Last active October 3, 2022 16:45
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save msewell/5e185518a553b7ba9743451b5b817b31 to your computer and use it in GitHub Desktop.
Save msewell/5e185518a553b7ba9743451b5b817b31 to your computer and use it in GitHub Desktop.
SegueHandlerType for Swift 3
/* The SegueHandlerType pattern, as seen on [1, 2], adapted for the changed Swift 3 syntax.
[1] https://developer.apple.com/library/content/samplecode/Lister/Listings/Lister_SegueHandlerType_swift.html
[2] https://www.natashatherobot.com/protocol-oriented-segue-identifiers-swift/
*/
protocol SegueHandlerType {
// `typealias` has been changed to `associatedtype` for Protocols in Swift 3.
associatedtype SegueIdentifier: RawRepresentable
}
extension SegueHandlerType where Self: UIViewController, SegueIdentifier.RawValue == String {
// This used to be `performSegueWithIdentifier(...)`.
func performSegue(withIdentifier identifier: SegueIdentifier, sender: Any?) {
performSegue(withIdentifier: identifier.rawValue, sender: sender)
}
func segueIdentifier(for segue: UIStoryboardSegue) -> SegueIdentifier {
guard let identifier = segue.identifier, let segueIdentifier = SegueIdentifier(rawValue: identifier) else {
fatalError("Couldn't handle segue identifier \(String(describing: segue.identifier)) for view controller of type \(type(of: self)).")
}
return segueIdentifier
}
}
import UIKit
class ViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segueIdentifier(for: segue) {
case .showFoo:
// prepare for segue to Foo
break
case .showBar:
// prepare for segue to Bar
break
}
}
}
/* We're using a protocol extension here to separate UIViewController concerns.
See [1, 2] for more info on this.
[1] https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID521
[2] https://www.natashatherobot.com/using-swift-extensions/
*/
extension ViewController: SegueHandlerType {
enum SegueIdentifier: String {
case showFoo
case showBar
}
}
@farzadshbfn
Copy link

farzadshbfn commented Jan 9, 2017

I think it's better to turn:

func segueIdentifierForSegue(segue: UIStoryboardSegue) -> SegueIdentifier {
        guard let identifier = segue.identifier, let segueIdentifier = SegueIdentifier(rawValue: identifier) else {
            fatalError("Couldn't handle segue identifier \(segue.identifier) for view controller of type \(type(of: self)).")
        }

        return segueIdentifier
    }

into

func segueIdentifier(forSegue segue: UIStoryboardSegue) -> SegueIdentifier {
        guard let identifier = segue.identifier, let segueIdentifier = SegueIdentifier(rawValue: identifier) else {
            fatalError("Couldn't handle segue identifier \(segue.identifier) for view controller of type \(type(of: self)).")
        }

        return segueIdentifier
    }

and also ShowFoo -> showFoo, ShowBar -> showBar
It's more Swift3 like

@chrisscholly
Copy link

Even better to turn:

func segueIdentifier(forSegue segue: UIStoryboardSegue) -> SegueIdentifier {
    ...
}

into:

func segueIdentifier(for segue: UIStoryboardSegue) -> SegueIdentifier {
    ...
}

😄

@msewell
Copy link
Author

msewell commented Apr 28, 2017

You're both quite right! I've updated the Gist.

@d108
Copy link

d108 commented Jun 12, 2017

@msewell, thanks for sharing your code.

When using an unwind segue, the segue identifier is nil. The following modifications can take care of this while still catching missing SegueIdentifier cases.

func segueIdentifier(for segue: UIStoryboardSegue) -> SegueIdentifier? {
    guard let identifier = segue.identifier else { 
        return nil 
    }

    guard let segueIdentifier = SegueIdentifier(rawValue: identifier) else {
        fatalError("Couldn't handle segue identifier \(identifier) for view controller of type \(type(of: self)).")
    }

    return segueIdentifier
}

@msewell
Copy link
Author

msewell commented Jul 21, 2017

@d108

When using an unwind segue, the segue identifier is nil.

Are you sure about that? I believe you can give your unwind segue an identifier in Interface Builder.

@simongardener
Copy link

I could be doing something wrong here (xcode 9.1 swift 4) but if i write the performSegue method as you do

    func performSegue(withIdentifier identifier: SegueIdentifier, sender: Any?) {
        performSegue(withIdentifier: identifier.rawValue, sender: sender)
    }

then when t type this
performSegue(withIdentifier: .measurements , sender: self)

I got no autocomplete after typing the period. I would have to explicitly add SegueIdentifier. to get the autocomplete to kickin

if, in the protocol extension, I change the performSegue to a different form, say by replacing the withIdentifier to _ as here
func performSegue(_ identifier: SegueIdentifier, sender: Any?) { performSegue(withIdentifier: identifier.rawValue, sender: sender) }
then I don't need to type SegueIdentifier but just start with .m and the autocomplete kicks in

the lack of Xcode autocompleting is a downside bigger than the benefit of having a method with the same parameter names Imho.

Of course, this is possibly just another Xcode bug :)

@aureliano2nd
Copy link

aureliano2nd commented Dec 12, 2018

Hi, thanks for the gist !

the switch statement would be swiftier without the break statements

class ViewController: UIViewController {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        switch segueIdentifier(for: segue) {
        case .showFoo:
            // prepare for segue to Foo
        case .showBar:
            // prepare for segue to Bar
        }
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment