Coordinators in practice

Navigation Coordinator: the superclass

//
//  NavigationCoordinator.swift
//

import Foundation
import UIKit

protocol NavigationCoordinatorDelegate: class {
  func rootwillAppear(_ root:UIViewController)
  func present(_ vc:UIViewController, onto frame:CGRect)
  func detailViewWillAppear()
  func masterViewWillAppear()
}

class NavigationCoordinator {
  var nav: UINavigationController
  weak var delegate: NavigationCoordinatorDelegate?
  var rootVC: UIViewController { return nav.viewControllers.first! }
  
  init(vc: UIViewController) {
    self.nav = UINavigationController(rootViewController:vc)
  }
  func popToRoot() {
    nav.popToRootViewController(animated: false)
  }
  func push(_ vc: UIViewController) {
    nav.pushViewController(vc, animated: true)
  }
  func hideNavBar() {
    nav.navigationBar.isHidden = true
  }
  func showNavBar() {
    nav.navigationBar.isHidden = false
  }
  func topDistance() -> CGFloat {
    return nav.navigationBar.frame.size.height + UIApplication.shared.statusBarFrame.height
  }
  
}

Main Coordinator: inherits, and specifies the flow

protocol MainCoordinatorDelegate: NavigationCoordinatorDelegate {
  func didDismissSettings()
  func settingsButtonTapped()
}

class MainCoordinator: NavigationCoordinator {
  
  override init(vc: UIViewController) {
    let mainVC = vc as! MainVC
    super.init(vc:mainVC)
    mainVC.delegate = self
  }

}

extension MainCoordinator: MainVCDelegate {

  func willAppear() {
    delegate?.rootwillAppear(nav.viewControllers.first!)
    nav.navigationBar.isHidden = false
  }

  func didTapSettingsButton() {
    (delegate as? MainCoordinatorDelegate).settingsButtonTapped()
    let settingsVC = SettingsVC()
    settingsVC.delegate = self
    nav.pushViewController(settingsVC, animated: true)
  }

}  

extension MainCoordinator: SettingsVCDelegate {
  //TODO:
}

Now MainVC and SettingsVC have no references to each other or to its parent view controllers. Hooray!

Thanks to Khanlou Video