0

I want to instantiate a viewController with a container with the following:

let vc = self.storyboard?.instantiateViewController(withIdentifier: ContainerViewController") as? ContainerViewController

I also need a reference to the containerView so I try the following:

let vc2 = vc.childViewControllers[0] as! ChildViewController

The app crashes with a 'index 0 beyond bounds for empty NSArray'

How can I instantiate the containerViewController and it's childViewController at the same time prior to loading the containerViewController?

EDIT

The use case is for AWS Cognito to go to the signInViewController when the user is not authenticated. This code is in the appDelegate:

func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
    if self.containerViewController == nil {
        self.containerViewController = self.storyboard?.instantiateViewController(withIdentifier: "ContainerViewController") as? ContainerViewController
    }
    if self.childViewController == nil {
        self.childViewController = self.containerViewController!.childViewControllers[0] as! ChildViewController
    }

    DispatchQueue.main.async {
        self.window?.rootViewController?.present(self.containerViewController!, animated: true, completion: nil)
    }
    return self.childViewController!
}

The reason I am instantiating the container and returning the child is that the return needs to conform to the protocol which only the child does. I suppose I can remove the container but it has functionality that I would have wanted.

alionthego
  • 8,508
  • 9
  • 52
  • 125

1 Answers1

0

Short answer: You can't. At the time you call instantiateViewController(), a view controller's view has not yet been loaded. You need to present it to the screen somehow and then look for it's child view once it's done being displayed.

We need more info about your use-case in order to help you.

EDIT:

Ok, several things:

If your startPasswordAuthentication() function is called on the main thread, there's no reason to use DispatchQueue.main.async for the present() call.

If, on the other hand, your startPasswordAuthentication() function is called on a background thread, the call to instantiateViewController() also belongs inside a DispatchQueue.main.async block so it's performed on the main thread. In fact you might just want to put the whole body of your startPasswordAuthentication() function inside a DispatchQueue.main.async block.

Next, there is no way that your containerViewController's child view controllers will be loaded after the call to instantiateViewController(withIdentifier:). That's not how it works. You should look for the child view in the completion block of your present call.

Next, you should not be reaching into your containerViewController's view hierarchy. You should add methods to that class that let you ask for the view you are looking for, and use those.

If you are trying to write your function to synchronously return a child view controller, you can't do that either. You need to rewrite your startPasswordAuthentication() function to take a completion handler, and pass the child view controller to the completion handler

So the code might be rewritten like this:

func startPasswordAuthentication(completion: @escaping (AWSCognitoIdentityPasswordAuthentication?)->void ) {
    DispatchQueue.main.async { [weak self] in
      guard strongSelf = self else {
        completion(nil)
        return
      }
      if self.containerViewController == nil {
        self.containerViewController = self.storyboard?.instantiateViewController(withIdentifier: "ContainerViewController") as? ContainerViewController
      }

      self.window?.rootViewController?.present(self.containerViewController!, animated: true, completion: {
        if strongSelf == nil {
          strongSelf.childViewController = self.containerViewController.getChildViewController()
        }
        completion(strongSelf.childViewController)
      }
    })
}

(That code was typed into the horrible SO editor, is totally untested, and is not meant to be copy/pasted. It likely contains errors that need to be fixed. It's only meant as a rough guide.)

Community
  • 1
  • 1
Duncan C
  • 128,072
  • 22
  • 173
  • 272