uber/needle

How to solve circular dependency?

Ewg777 opened this issue · 6 comments

Here is an example of a component


class ABCComponent: Component<EmptyDependency>{


    var a: AProtocol {
        A(b)
    }

    var b: BProtocol {
        B(c)
    }

    var c: CProtocol {
        C(a)
    }


Now needle stucks in runtime try to resolve it. Is there a way to fix it?
Thanks

Full example


@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        registerProviderFactories()
        let rootComponent = RootComponent()
        let aaa = rootComponent.aaa
        print(aaa)
...

protocol AProtocol {
}

protocol BProtocol {
}

protocol CProtocol {
}

class A: AProtocol {
    init(b: BProtocol) {}
}

class B: BProtocol {
    init(c: CProtocol) {}
}

class C: CProtocol {
    init(a: AProtocol) {}
}

class ABCComponent: Component<EmptyDependency> {

    var a: AProtocol {
        A(b: b)
    }

    var b: BProtocol {
        B(c: c)
    }

    var c: CProtocol {
        C(a: a)
    }
}


final class RootComponent: BootstrapComponent {

    var abc: ABCComponent {
        ABCComponent(parent: self)
    }

    var aaa: AProtocol {
        abc.a
    }
}

@rudro @neakor could you help please?

rudro commented

Thanks for the full example, but I don't think this has anything to do with Needle. Consider this code:

class ABCComponent {

    var a: AProtocol {
        A(b: b)
    }

    var b: BProtocol {
        B(c: c)
    }

    var c: CProtocol {
        C(a: a)
    }

     init() {}
}

let c = ABCComponent()
print(c.a)

Even if there's no Needle, this print statement will run into the same issue.

@rudro thanks for your feedback!
Let me give you one more example. Here is more realistic example. Please let me know if it's possible to resolve with Needle.

protocol ViewControllerProtocol: AnyObject {
}

protocol PresenterProtocol {
}

protocol RouterProtocol {
}

class AViewController: ViewControllerProtocol {
    var presenter: PresenterProtocol!
}

class Presenter: PresenterProtocol {
    var router: RouterProtocol!
}

class Router: RouterProtocol {
    weak var controller: ViewControllerProtocol?

    init(controller: ViewControllerProtocol) {
        self.controller = controller
    }
}

class ABCComponent {

    var aViewController: ViewControllerProtocol!
}

Legit question. It's of course not possible to resolve circular dependency statically (by the very nature of the problem), but usually these use cases can be dealt with the usage of lazy injection, i.e. one of the dependencies in the circle is not resolved statically, but only injected upon usage. Needle seems to be inspired by dagger somewhat, so this link might help.