This article has been translated into Chinese
Universal Responsive Transitions with RxSwiftWelcome to
“Gold Digging Translation Project”Translation of quality technical articles.

Personaly I like the idea behind UIStoryboardSegue of decomposition navigation logic from your business logic routine.

But I’m not a fun of using it with storyboard and I’ve never created them programmatically.

So decided to adopt it feature and create something similar.

But what fields this type should have?

It definitely should have

 let fromViewController:UIViewController Copy the code

Most of us passing some context (or not) which we want to display on the next ViewController.

Because swift have such a cool feature called Generics lets take advantage of this and make our context to be a generic type T

Also, we some how need to create toViewController object. I decided to do this with block, lets call it

let toViewControllerFactory:(context:T) -> UIViewController

And now we are ready to implement “reactive” part of our segues

There are two things that segue could do — it could push new viewController or present it modally.

private(set) lazy var pushObserver:AnyObserverCopy the code
private(set) lazy var presentObserver:AnyObserverCopy the code

So lets implement these observers

import UIKit
import RxSwift

class Segue {
   
   private(set) weak var fromViewController:UIViewController?
   let toViewControllerFactory:(context:T) -> UIViewController
   
   init(fromViewController:UIViewController,
      toViewControllerFactory:(context:T) -> UIViewController) {
         self.fromViewController = fromViewController
         self.toViewControllerFactory = toViewControllerFactory
   }
   
   private(set) lazy var pushObserver:AnyObserver = AnyObserver {[weak self] event in
      switch event {
      case .Next(let value):
         guard let strong = self else {return}
         let toViewController = strong.toViewControllerFactory(context: value)
         strong.fromViewController?.navigationController?
            .pushViewController(toViewController, animated:true)
      default:
         break
      }
   }
   
   private(set) lazy var presentObserver:AnyObserver = AnyObserver {[weak self] event in
      switch event {
      case .Next(let value):
         guard let strong = self else {return}
         let toViewController = strong.toViewControllerFactory(context: value)
         strong.fromViewController?.presentViewController(toViewController, animated: true, completion: nil)
      default:
         break
      }
   }
   
}Copy the code

Note: If you don’t want to pass the context within segue just create it with Void type

lazy var segue:Segue 

And now we can user our Segue some thing like this:

import RxSwift
import RxCocoa

class SomeTableViewController:UITableViewController {
  let disposeBag = DisposeBag()
  let items:[Item] ...
  
  lazy var itemDetailsSegue:Segue = {
      return Segue(fromViewController: self,
              toViewControllerFactory: { context -> UIViewController in
                  return ItemDetailsViewController(item:context)
              })
  }
  
    lazy var voidModalSegue:Segue = {
      return Segue(fromViewController: self,
              toViewControllerFactory: { _ -> UIViewController in
                  return SomeViewController()
              })
  }
  
  
    override func viewDidLoad() {
      super.viewDidLoad()
      
      someButton.rx_tap
            .bindTo(voidModalSegue.presentObserver)
            .addDisposableTo(disposeBag)
      
      tableView.rx_itemSelected
            .map({[unowned self] indexPath in self.items.[indexPath.row]})
            .bindTo(itemDetailsSegue.pushObserver)
            .addDisposableTo(disposeBag)
    }
  
}Copy the code

What it gives? It separate your navigation logic, and also this class could be easily covered with unit test.

Ideas, remarks? Will be happy to discuss in comments 😉