AliSoftware / Dip

Simple Swift Dependency container. Use protocols to resolve your dependencies and avoid singletons / sharedInstances!
MIT License
978 stars 75 forks source link

Crash due to IBOutlets as nil, viewController resolved in tableView:didSelectRowAt:, used DipUI #170

Closed Daemon-Devarshi closed 7 years ago

Daemon-Devarshi commented 7 years ago

I tried DIP in a sample app, which has two view controllers:

  1. EmployeesViewController - contains list view
  2. EmployeeDetailViewController - detail view is loaded when a cell on previous list view is selected

I am setting up the dependencies using below code:

// CompositionRoot.swift
extension DependencyContainer {
    static func configure() -> DependencyContainer {
        return DependencyContainer { container in
            container.register { EmployeesViewModel() as EmployeesDataSource }
            container.register(tag: EmployeesViewController.storyboardIdentifier) { EmployeesViewController() }.resolvingProperties { container, controller in
                controller.dataSource = try container.resolve() as EmployeesDataSource
                controller.container = container
            }
            container.register { employee in EmployeeViewModel(employee: employee) as PresentableEmployeeInfo }
            container.register(tag: EmployeeDetailViewController.storyboardIdentifier) { EmployeeDetailViewController() as EmployeeDetailViewController }

            DependencyContainer.uiContainers = [container]
        }
    }
}

extension EmployeesViewController:  StoryboardInstantiatable { }
extension EmployeeDetailViewController:  StoryboardInstantiatable { }

I am trying to load EmployeeDetailViewController from tableView:didSelectRowAt: function as shown below:

// EmployeesViewController.swift
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let employee = dataSource.employee(at: indexPath.row)
        let employeeInfo = try! container.resolve(arguments: employee) as PresentableEmployeeInfo

        // Below code works and does not crash in EmployeeDetailViewController-viewDidLoad
        //let storyboard = UIStoryboard(name: "Main", bundle: nil)
        //let detailViewController = storyboard.instantiateViewController(withIdentifier: "EmployeeDetailViewController") as! EmployeeDetailViewController
        // Below code crashes in EmployeeDetailViewController-viewDidLoad due to IBOutlets as nil (if used in place of above code)
        let detailViewController = try! container.resolve(tag: EmployeeDetailViewController.storyboardIdentifier) as EmployeeDetailViewController
        detailViewController.employeeInfo = employeeInfo
        navigationController?.pushViewController(detailViewController, animated: true)
    }

Problem is - It crashes in EmployeeDetailViewController-viewDidLoad due to nil IBOutlets. It works fine when I load the same view controller using standard functions.

Here is the link to its codebase - DIPSample.

Am I missing anything over here? Please suggest.

ilyapuchka commented 7 years ago

You are using Dip-UI in a wrong way. If you register view controller that should be created from storyboard you should not explicitly resolve it with container, you should create it in a usual manner - using a storyboard. Container then will resolve its dependencies with property injection. If you want to resolve view controller explicitly you should register it properly with UIStoryboard.instantiateViewController(withIdentifier:) or with UIViewController.init(nibName:bundle:) and view controller defined in xib.