'Swift multiple dependency injection using protocols

I've been diving into dependency injection. I understand the why of DI, but I can't seem to grasp how to implement it in a graceful way. I'm especially looking to make more use of protocols and without the use of a third-party framework. I've been reading up about things like AbstractFactory, which makes sense, but whenever I'm trying to implement it in my multiview app I just seem to get lost. Using storyboards already makes it a bit harder and also the fact that some dependencies have themselves dependencies, makes me feel like I'm doing Inception programming.

So I will now give you a simplified version of the app with 2 screens and a very hacky kind of way of injecting dependencies. I don't think it's even real DI.

So it's a Real estate app. Fetching houses from a network API.

I have a simple House datamodel

struct House {
    let title: String
}

I have 2 protocols, NetworkFetchable and LocationManageable and 2 classes that implement them. However this is already where the fun starts. Suppose that the LocationClass also has a NetworkFetchable dependency :

protocol NetworkFetchable {
    func fetchData(completionHandler: @escaping(House) -> Void)
}

protocol LocationManageable {
    func fetchLocation()
}

class NetworkClass: NetworkFetchable {
    func fetchData(completionHandler: @escaping(House) -> Void) {
        // Fetches data from API
    }
}

class LocationClass: LocationManageable {
    
    let network: NetworkFetchable
    
    init(network: NetworkFetchable) {
        self.network = network
    }
    
    func fetchLocation() {
        // Fetch current location
    }
}

There's 2 Views. HouseViewController is nested in a RootNavigationController. AboutViewController is not. Both RootNavigationController and AboutViewController are nested in a TabBarController. They both have a view model, which also have dependencies. Now as you can see, it's already very messy. Optional view models etc. And the dependencies aren't injected at the initialisation. But this is what I could come up with... I'm really looking for someone to show how this is done. And I appreciate your time to look at this. Thank you very much.

class RootNavigationController: UINavigationController {
    
    private var locationClass: LocationManageable!
    private var networkClass = NetworkClass()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        locationClass = LocationClass(network: networkClass)
        if let houseViewController = children[0] as? HouseViewController {
            houseViewController.houseViewModel = HouseViewModel(locationService: locationClass, networkService: networkClass)
        }
        if let aboutViewController = tabBarController?.viewControllers?[1] as? AboutViewController {
            aboutViewController.aboutViewModel = AboutViewModel(locationService: locationClass)
        }
    }
    
}

class HouseViewController: UIViewController {
    
    var houseViewModel: HouseViewModel?
    
}

class AboutViewController: UIViewController {
    
    var aboutViewModel: AboutViewModel?
    
} 

class HouseViewModel {
    
    let locationService: LocationManageable
    let networkService: NetworkFetchable
    
    init(locationService: LocationManageable, networkService: NetworkFetchable) {
        self.locationService = locationService
        self.networkService = networkService
    }
    
}

class AboutViewModel {
    
    let locationService: LocationManageable
    
    init(locationService: LocationManageable) {
        self.locationService = locationService
    }
    
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source