TabBarController: Dependency injection doesn't inject into subViewController

94 views Asked by At

I read a tutorial denpendency injection. And try to apply it into my app to pass the state into each subViewControllers of tabBarController in my case. But the modelController returns nil in subVC of tabBarController, I think there is something wrong in the passing of modelController instance, but I couldn't find it per several hours check...

Well, any help/ hint is highly appreciated.

First, I create an instance of ModelController in SceneDelegate since it is the rootViewController.

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let _ = (scene as? UIWindowScene) else { return }
        
        guard let rootViewController = window?.rootViewController as? TabBarController else {
            fatalError("Unexpected Root View Controller")
        }

        rootViewController.modelController = ModelController()
    }

Then for test purpose, I inject it into one of subView of tabBarController(SongsViewController).

class TabBarController: UITabBarController {
    var modelController: ModelController!

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let viewControllers = viewControllers else {
            return
        }
        
        for viewController in viewControllers {
            switch viewController {
            case let viewController as SongsViewController:
                viewController.modelController = modelController
            default:
                break
            }
        }
    }
}

Finally, in SongsViewController, I test modelController in viewWillAppear but it returns nil.

class SongsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet var table: UITableView!
    
    // data source
    var songs = [Song]()
    
    // dependency injection from model controller
    var modelController: ModelController!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        table.delegate = self
        table.dataSource = self
        table.rowHeight = 66
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        guard modelController != nil else {
            fatalError("modelController is nil")
        }
        
        loadData()
    }
1

There are 1 answers

0
Zhou Haibo On

Add the solution code in case anyone may meet this after, it may save your precious time.

Basically say the 1st level we got is the navigationControllers, then we should go deeper to find that child view in navigationController. In my case, the 1st child of navigationController is the VC I want to access naviController.viewControllers[0].

TabBarController -> NavigationControllers -> subViewControllers

So change the code per your scenario.

class TabBarController: UITabBarController {
    var modelController: ModelController!

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let viewControllers = viewControllers else {
            return
        }
        
        if let naviController = viewControllers[1] as? UINavigationController {
            if let vc = naviController.viewControllers[0] as? SongsViewController {
                vc.modelController = modelController
            }
        }
       
    }
}