coordinator is nil and not navigating to the next screen on button click

727 views Asked by At

I have been trying to refactor my source code so that it would conform to the Coordinator Pattern. I have used UITabBarController as the parent viewController of my app which contains 4 viewControllers.

I have been following the tutorials on how to implement the Coordinator pattern for iOS apps, and I have created and set up the protocols and classes of the Coordinator. I have a button inside my viewController (child viewController of the TabbarViewController), however, on button click, coordinator is not pushing / navigating to the desired VC, and I see the coordinator is returning nil on the debug console while debugging through the breakpoint, and I could not figure it out how to resolve this issue.

MainCoordinator.swift:

class MainCoordinator: SubCoordinator {
    var subCoordinators = [SubCoordinator]()
    var navigationController: UINavigationController
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    func start() {
        print("Initialized.. .")
        UIApplication.app().window?.rootViewController = self.navigationController
        let vc = SplashViewController.instantiate()
        vc.coordinator = self
        navigationController.pushViewController(vc, animated: false)
    }
        }
   // testing using a simple Viewcontroller class, its background color is set to red, so if the  
   // navigation works, a blank red VC should appear. but not working so far
    func testView() {
        let vc = ViewController.instantiate()
        vc.coordinator = self
        navigationController.pushViewController(vc, animated: false)
       
    }
}

SubCoordinator.swift:

protocol SubCoordinator {
    var subCoordinators: [SubCoordinator] { get set }
    var navigationController: UINavigationController { get set }
    
    func start()
}

StoryBoarded.swift:

protocol StoryBoarded {
    static func instantiate() -> Self
}
  // I am using storyBoard, and `instantiate()` should instantiate and return the specified VC 
  // from the Storyboard with the specified VC id (?)
extension StoryBoarded where Self: UIViewController {
    static func instantiate() -> Self {
        let id = String(describing: self)
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        return storyboard.instantiateViewController(withIdentifier: id) as! Self
    }
}

FirstViewController.Swift:

class FirstViewController: UIViewController, StoryBoarded {
    @IBOutlet weak var button: UIButton!
    var coordinator: MainCoordinator?
    //MARK: - viewDidLoad()
    override func viewDidLoad() {
        super.viewDidLoad()
   // If uncommented the below line, coordinator is not returning `nil`, but not navigating 
      anyways!
        //coordinator = MainCoordinator(navigationController: UINavigationController())
    }
    @IBAction func onButtonTap(_ sender: Any) {
   // So, basically I would expect the coordinator to navigate to the testView, but not 
      navigating
        coordinator?.testView()
    }
}

ViewController.swift:

// testView
class ViewController: UIViewController, StoryBoarded {
    var coordinator: MainCoordinator?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.view.backgroundColor = .red
    }

}

and

// TabbarController, set as the root VC after the splashVC is completed
class MainViewController: UITabBarController, StoryBoarded {
    var coordinator: MainCoordinator?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let firstVC = UIStoryboard.firstViewController()
        let secondVC = UIStoryboard.secondViewController()
        
        let views: [UIViewController] = [firstVC, secondVC]
        self.setViewControllers(views, animated: false)
        self.navigationController?.navigationBar.isHidden = false
    }
    
}

start() is being called, and splashVC appears and updates rootViewController with MainViewontroller on completion, But the navigation is not working at all on button click event.

Any feedback or help would highly be appreciated!

2

There are 2 answers

1
Don On

Since you're using the StoryBoarded protocol, you should follow the pattern and call instantiate() for initialization. Then, just set the coordinator.

class MainViewController: UITabBarController, StoryBoarded {
  var coordinator: MainCoordinator?

  override func viewDidLoad() {
    super.viewDidLoad()
    let firstVC = FirstViewController.instantiate()
    let secondVC = SecondViewController.instantiate()

    firstVC.coordinator = self.coordinator
    secondVC.coordinator = self.coordinator
    
    let views: [UIViewController] = [firstVC, secondVC]
    self.setViewControllers(views, animated: false)
    self.navigationController?.navigationBar.isHidden = false
  }
}
0
Max On

Got this working.

refer : https://www.youtube.com/watch?v=Tb3PlVFq3YY, Time : 6.59

For some reason, I could not get my project working as the coordinator was nil and I could see a black screen. On looking at other examples I got it working by moving the window set up inside the SceneDelegate.swift under the willConnectTo method. I had to also add this extra line to make things work window?.windowScene = windowScene

 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }
        
        guard let windowScene = (scene as? UIWindowScene) else {
            return
        }
        
    
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.windowScene = windowScene
        
        let navController = UINavigationController()
        coordinator = MainCoordinator(navigationController: navController)
        coordinator?.start()
        
        window?.rootViewController = navController
        window?.makeKeyAndVisible()
    }

refer enter image description here enter image description here