Composition Root

A classic solution to the problem of creating the dependencies is using a pattern called Composition Root.

The Composition Root is a place in the code where components of different layers are wired together. The reason to have in a separate place is to not mix configuration logic and the other different kind of logic, like the business logic that defines the behavior of the software. The only responsibility of the Composition Root pattern is to instantiate and configure other components. The Injections of the dependencies, either in the constructor or in properties, must be done in the Composition Root.
In the following diagram, we can see that the Composition Root is a particular layer that has the responsibility of composing the modules.

Ideally, only one Composition Root should be present in the app, and be started at the launch of the app. However, it shouldn't be a single class or a single function, but it can be multiple classes with multiple functions; it really depends on the complexity and the preferences of the developers.

Let's use a simple e-commerce app as an example:

class AppDependencies {
private var basketController: BasketController!
private var productsController: ProductsController!

init() {
configureDependencies()
}

private func configureDependencies() {
let configuration = Configuration.loadFromBundleId()
let apiService = NetworkApi(with: configuration)
self.basketController = createBasketController(apiService: apiService, configuration: configuration)
self.productsController = createProductsController(apiService: apiService)
}

private func createBasketController(apiService: NetworkApi, configuration: Configuration) -> BasketController {
let basketService = RestBasketService(apiService: apiService)
let basketStore = BasketCoreData(with: configuration)
return BasketController(service: basketService, store: basketStore)
}

private func createProductsController(apiService: NetworkApi) -> ProductsController {
let productsService = RestProductsService(apiService: apiService)
return ProductsController(service: productsService)
}
}

The AppDependecies class creates all the needed objects and links them together. In this case, the objects are created at the start of the app, but if something depends on a product created during the flow, for example, the ID of a product selected from a list, AppDependencies could expose the needed functions. Also, AppDependencies could inject itself inside the created objects.

However, back in this simple example, now we create the instance of AppDependencies in the app delegate:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let dependencies = AppDependencies()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
dependencies.installRootViewControllerIntoWindow(window: window)
return true
}
}

AppDependencies then expose an entry point with window as an argument:

class AppDependencies {
//...
func installRootViewControllerIntoWindow(window: UIWindow?) {
guard let mainViewController = window?.rootViewController as? MainViewController else {
fatalError("The rootViewController must be a MainViewController")
}
mainViewController.basketController = basketController
mainViewController.productsController = productsController
}

//...
}

This pattern is simple and effective, but its major downside is that it grows when the app becomes bigger. In general, this solution works well though. 

"The biggest challenge of properly implementing DI is getting all classes with dependencies moved to Composition Root."
–Mark Seeman, Dependency Injection in NET, 5.3 - Constrained Construction