0

I have a UINavigationController with root HomeViewController(). HomeViewController has a property named someString. I want to access someString from another view controller that has been pushed onto the navigation stack programmatically:

navigationController?.pushViewController(DetailViewController(), animated: true)

In the DetailViewController that wants to access someString I am doing this:

var homeViewController: HomeViewController!

override func viewDidLoad() {
  super.viewDidLoad()

  let navController = self.navigationController!
  homeViewController = navController.viewControllers.first as? HomeViewController
}

And then to get the value of someString from within DetailViewController:

var stringFromRoot = homeViewController.someString

Is this the proper way to access properties of the root view controller in a UINavigationController? It does work in my testing, but if it has errors or drawbacks, or if there is a better way, I would like to know about that.

Rick Free
  • 143
  • 1
  • 10
  • It the root controller doesn't change, this should work. Avoid force unwrapping though (`!`) – Tamás Sengel Jan 03 '21 at 20:54
  • 1
    It's ok as a workaround, but as a design it's very fragile and "dirty" (since the dependency of DetailViewController on MainViewController is not expressed in any way; and what if MainViewController is no longer "first" in its navcontroller?). Consider passing the value to detail controller in the segue, or use a delegate pattern (pass HomeViewCOntroller to Detail as a protocol delegate representing the data you need). See a lot of other ideas here: https://stackoverflow.com/questions/5210535/passing-data-between-view-controllers – timbre timbre Jan 03 '21 at 21:51
  • @KirilS. I ended up taking your advice and learned to rewrite it with a delegate solution. If you want to answer with an example of that, I will accept your answer. Otherwise I will write that up and accept it. Thanks! – Rick Free Jan 04 '21 at 16:36

2 Answers2

1

This is the only way I know of accessing the root view controller in a UINavigationController. You asked about drawbacks, so here is one:

You can change the navigation controller's view controller's on the fly with setViewControllers(_:, animated:) so it might not be a good long term strategy to assume that the root view controller will always be HomeViewController

It looks like you're using your navigation controller as a singleton to store and retrieve constants. Unless someString depends on some value in your navigation controller, it would be more maintainable to extract that out to some kind of Constants struct.

pietrorea
  • 841
  • 6
  • 14
  • You're right, the example I posted does suggest it's using root controller as a constants store, and your response makes sense. In the actual code, though, the data is a freshly polled CLLocation that the root has (and uses) and other views need, too. I should have noted that, but my main concern was finding the right Apple/Swifty way to communicate between controllers. Seems like Delegate/Protocol is the answer I needed, and is how I ended up going. – Rick Free Jan 04 '21 at 16:42
  • 1
    Gotcha, for that particular case, I've seen people build singleton wrappers on top of `CLLocationManager` and save the most recently `CLLocation` in a property. Then you can get the location from any controller – pietrorea Jan 04 '21 at 17:41
0

self.navigationController!.viewControllers

This will give array of view controllers added into navigation stack you can loop over it and accesss any of ViewController property From it.

Apps Maven
  • 1,314
  • 1
  • 4
  • 17