         configure: ((T) -> Void)?,
         completion: ((T) -> Void)?) -> T?
     
+    /// Present a view controller from the specified storyboard in the primary context.
+    ///
+    ///     show(storyboard: "ShowSettings") { (controller: ShowSettingsViewController) in
+    ///         controller.someProperty = "Abc"
+    ///     }
+    ///
+    /// Set the initial view controller in the target storyboard or specify the identifier.
+    ///
+    /// - Parameters:
+    ///   - storyboard: Storyboard name.
+    ///   - bundle: Bundle of the storyboard.
+    ///   - identifier: Storyboard identifier.
+    ///   - configure: Configure the view controller before it is loaded.
+    /// - Returns: Returns the view controller instance from the storyboard.
     @discardableResult
     func show<T: UIViewController>(
-        storyboard: StoryboardIdentifier,
+        storyboard: String,
         inBundle bundle: Bundle?,
         identifier: String?,
         configure: ((T) -> Void)?) -> T?
     
+    /// Present a view controller from the specified storyboard in the secondary (or detail) context.
+    ///
+    /// Set the initial view controller in the target storyboard or specify the identifier.
+    ///
+    /// - Parameters:
+    ///   - storyboard: Storyboard name.
+    ///   - bundle: Bundle of the storyboard.
+    ///   - identifier: Storyboard identifier.
+    ///   - configure: Configure the view controller before it is loaded.
+    /// - Returns: Returns the view controller instance from the storyboard.
     @discardableResult
     func showDetailViewController<T: UIViewController>(
-        storyboard: StoryboardIdentifier,
+        storyboard: String,
         inBundle bundle: Bundle?,
         identifier: String?,
         configure: ((T) -> Void)?) -> T?
     
+    /// Selects the tab for the root controller of the window.
+    ///
+    ///     show(tab: 3) { (controller: ShowSettingsViewController) in
+    ///         controller.someProperty = "Abc"
+    ///     }
+    ///
+    /// Set the initial view controller in the target storyboard or specify the identifier.
+    ///
+    /// - Parameters:
+    ///   - tab: The tab to select.
+    ///   - window: The window where the root tab controller resides.
+    ///   - configure: Configure the view controller before it is loaded.
+    ///   - completion: Completion the view controller after it is loaded.
+    @discardableResult
+    func show<T: UIViewController>(
+        tab: Int,
+        for window: UIWindow?,
+        configure: ((T) -> Void)?,
+        completion: ((T) -> Void)?) -> T?
+    
+    /// Swaps the root controller of the main window.
+    ///
+    ///     root(storyboard: "ShowDashboard") { (controller: ShowDashboardViewController) in
+    ///         controller.someProperty = "Abc"
+    ///     }
+    ///
+    /// Set the initial view controller in the target storyboard or specify the identifier.
+    ///
+    /// - Parameters:
+    ///   - storyboard: Storyboard name.
+    ///   - window: The window where the root controller
+    ///   - window: The window where the root tab controller resides..
+    ///   - bundle: Bundle of the storyboard.
+    ///   - identifier: Storyboard identifier.
+    ///   - configure: Configure the view controller before it is loaded.
+    @discardableResult
+    func root<T: UIViewController>(
+        storyboard: String,
+        for window: UIWindow?,
+        inBundle bundle: Bundle?,
+        identifier: String?,
+        configure: ((T) -> Void)?) -> T?
+    
+    /// Adds a storyboard view controller as a child of the associated controller.
+    ///
+    ///     add(child: "ShowGraph") { (controller: ShowGraphViewController) in
+    ///         controller.someProperty = "Abc"
+    ///     }
+    ///
+    /// Set the initial view controller in the target storyboard or specify the identifier.
+    ///
+    /// - Parameters:
+    ///   - storyboard: The child view controller storyboard.
+    ///   - bundle: Bundle of the storyboard.
+    ///   - identifier: Storyboard identifier.
+    ///   - containerView: The target view, or nil to use the current view controller view.
+    ///   - configure: A block to run on the view controller after instantiation.
+    /// - Returns: The child view controller.
     @discardableResult
     func add<T: UIViewController>(
         child storyboard: String,

    {
        guard let viewController = viewController else { return nil }
        
        return RoutingLogic.showDetailViewController(
            delegate: viewController,
            storyboard: storyboard,
            inBundle: bundle,
            identifier: identifier,
            configure: configure
        )
    }
    
    /// Dismisses or pops the view controller that was presented.
    ///
    /// - Parameters:
    ///   - animated: Pass true to animate the presentation; otherwise, pass false.
    ///   - completion: The block to execute after the view controller is dismissed.
    func dismiss(animated: Bool = true, completion: (() -> Void)? = nil) {
        guard let viewController = viewController else { return }
        
        return RoutingLogic.dismiss(
            delegate: viewController,
            animated: animated,
            completion: completion
        )
    }
}

// MARK: - Private functions

fileprivate enum RoutingLogic {
    
    static func present<T: UIViewController>(
        delegate: UIViewController,
        storyboard: String,
        inBundle bundle: Bundle? = nil,
        identifier: String? = nil,
        animated: Bool = true,
        modalPresentationStyle: UIModalPresentationStyle? = nil,
        modalTransitionStyle: UIModalTransitionStyle? = nil,
        configure: ((T) -> Void)? = nil,
        completion: ((T) -> Void)? = nil) -> T?
    {
        let storyboard = UIStoryboard(name: storyboard, bundle: bundle)
        
        guard let controller = (identifier != nil
            ? storyboard.instantiateViewController(withIdentifier: identifier!)
            : storyboard.instantiateInitialViewController()) as? T else {
                assertionFailure("Invalid controller for storyboard \(storyboard).")
                return nil
        }
        
        if let modalPresentationStyle = modalPresentationStyle {
            controller.modalPresentationStyle = modalPresentationStyle
        }
        
        if let modalTransitionStyle = modalTransitionStyle {
            controller.modalTransitionStyle = modalTransitionStyle
        }
        
        configure?(controller)
        
        delegate.present(controller, animated: animated) {
            completion?(controller)
        }
        
        return controller
    }

    static func show<T: UIViewController>(
        delegate: UIViewController,
        storyboard: String,
        inBundle bundle: Bundle? = nil,
        identifier: String? = nil,
        configure: ((T) -> Void)? = nil) -> T?
    {
        let storyboard = UIStoryboard(name: storyboard, bundle: bundle)
        
        guard let controller = (identifier != nil
            ? storyboard.instantiateViewController(withIdentifier: identifier!)
            : storyboard.instantiateInitialViewController()) as? T else {
                assertionFailure("Invalid controller for storyboard \(storyboard).")
                return nil
        }
        
        configure?(controller)
        
        delegate.show(controller, sender: delegate)
        
        return controller
    }

    static func showDetailViewController<T: UIViewController>(
        delegate: UIViewController,
        storyboard: String,
        inBundle bundle: Bundle? = nil,
        identifier: String? = nil,
        configure: ((T) -> Void)? = nil) -> T?
    {
        let storyboard = UIStoryboard(name: storyboard, bundle: bundle)
        
        guard let controller = (identifier != nil
            ? storyboard.instantiateViewController(withIdentifier: identifier!)
            : storyboard.instantiateInitialViewController()) as? T else {
                assertionFailure("Invalid controller for storyboard \(storyboard).")
                return nil
        }
        
        configure?(controller)
        
        delegate.showDetailViewController(controller, sender: delegate)
        
        return controller
    }
    
    static func dismiss(delegate: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
        guard let navController = delegate.navigationController else {
            delegate.dismiss(animated: true, completion: completion)
            return
        }
        
        guard navController.viewControllers.count > 1 else {
            return navController.dismiss(animated: true, completion: completion)
        }
        
        navController.popViewController(animated: true, completion: completion)
    }
}

//
//  Created by Basem Emara on 4/26/17.
//  Copyright © 2017 Zamzam. All rights reserved.
//  http://basememara.com/protocol-oriented-router-in-swift/
//

import UIKit

/// Provides routing functionality for a type to remove navigation responsibility off `UIViewController`.
///
///     struct MyRouter: Router {
///         weak var viewController: UIViewController?
///
///         init(viewController: UIViewController?) {
///             self.viewController = viewController
///         }
///
///         func showSettings(date: Date) {
///             present(storyboard: "ShowSettings") { (controller: ShowSettingsViewController) in
///                 controller.someProperty = date
///             }
///         }
///     }
///
///     class MyViewController: UIViewController {
///
///         private lazy var router: Router = MyRouter(
///             viewController: self
///         )
///
///         @IBAction func settingsTapped() {
///             router.showSettings(date: Date())
///         }
///     }
///
/// Conforming types should use a weak `UIViewController` instance.
public protocol Router {
    var viewController: UIViewController? { get set }
    
    /// Presents a view controller from the specified storyboard modally.
    ///
    ///     present(storyboard: "ShowSettings") { (controller: ShowSettingsViewController) in
    ///         controller.someProperty = "Abc"
    ///     }
    ///
    /// Set the initial view controller in the target storyboard or specify the identifier.
    ///
    /// - Parameters:
    ///   - storyboard: Storyboard name.
    ///   - bundle: Bundle of the storyboard.
    ///   - identifier: Storyboard identifier.
    ///   - animated: Pass true to animate the presentation; otherwise, pass false.
    ///   - modalPresentationStyle: The presentation style for modally presented view controllers.
    ///   - modalTransitionStyle: The transition style to use when presenting the view controller.
    ///   - configure: Configure the view controller before it is loaded.
    ///   - completion: Completion the view controller after it is loaded.
    /// - Returns: Returns the view controller instance from the storyboard.
    @discardableResult
    func present<T: UIViewController>(
        storyboard: String,
        inBundle bundle: Bundle?,
        identifier: String?,
        animated: Bool,
