Lately I’ve been experimenting a lot with doing things differently in Swift. I’m still trying to find best practices and discover completely new ways of doing things. One example of this is passing objects from one view controller to another through a segue in a single line of code, which I will cover in this post.
Imagine two view controllers, a BookViewController and an AuthorViewController. Both are in the same storyboard and the BookViewController has a button that pushes the AuthorViewController on the navigation controller through a segue. To know which author we need to show on the AuthorViewController we need to pass an author object from the BookViewController to the AuthorViewController. The traditional way of doing this is giving the segue an identifier and then setting the object:
[objc]
class BookViewController: UIViewController {
var book: Book!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowAuthor" {
let authorViewController = segue.destinationViewController as! AuthorViewController
authorViewController.author = book.author
}
}
}
class AuthorViewController: UIViewController {
var author: Author!
}
[/objc]
And in case we would use a modal segue that shows a AuthorViewController embedded in a navigation controller, the code would be slightly more complex:
[objc]
if segue.identifier == "ShowAuthor" {
let authorViewController = (segue.destinationViewController as! UINavigationController).viewControllers[0] as! AuthorViewController
authorViewController.author = book.author
}
[/objc]
Now let’s see how we can add an extension to UIStoryboardSegue that makes this a bit easier and works the same for both scenarios. Instead of checking the segue identifier we will just check on the type of the destination view controller. We assume that based on the type the same object is passed on, even when there are multiple segues going to that type.
[objc]
extension UIStoryboardSegue {
func destinationViewControllerAs<T>(cl: T.Type) -> T? {
return destinationViewController as? T ?? (destinationViewController as? UINavigationController)?.viewControllers[0] as? T
}
}
[/objc]
What we’ve done here is add the method destinationViewControllerAs to UIStoryboardSegue that checks if the destinationViewController is of the generic type T. If it’s not, it will check if the destinationViewController is a navigation controller and if it’s first view controller is of type T. If it finds either one, it will return that instance of T. Since it can also be nil, the return type is an optional T.
It’s now incredibly simple to pass on our author object to the AuthorViewController:
[objc]
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
segue.destinationViewControllerAs(AuthorViewController.self)?.author = book.author
}
[/objc]
No need to check any identifiers anymore and less code. Now I’m not saying that this is the best way to do it or that it’s even better than the traditional way of doing things. But it does show that Swift offers us new ways of doing things and it’s worth to experiment to find best practices.
The source code of the samples and extension is available on GitHub – lammertw/StoryboardSegueExtension.