CyberDude
CyberDude

Reputation: 9009

Circular dependency issue with Swinject

I'm using Swinject in my Swift iOS app and try doing a circular dependency as explained on the official documentation page: https://github.com/Swinject/Swinject/blob/master/Documentation/CircularDependencies.md

I've copied the code from the circular dependency example and added a few things in the classes, didn't modify anything in the dependency registration code.

Classes and protocols look like this:

protocol ParentType: AnyObject {
    func getMyName() -> String
    func getMyChildName() -> String
}

protocol ChildType: AnyObject {
    func getMyName() -> String
    func getMyParentName() -> String
}

class Parent: ParentType {
    let child: ChildType?
    let name = "John"

    init(child: ChildType?) {
        self.child = child
    }

    func getMyName() -> String {
        return name
    }

    func getMyChildName() -> String {
        return child!.getMyName()
    }
}

class Child: ChildType {
    weak var parent: ParentType?
    let name = "John Jr."

    func getMyName() -> String {
        return name
    }

    func getMyParentName() -> String {
        return parent!.getMyName()
    }
}

Dependency configuration code looks like this (unchanged from example):

let container = Container()
container.register(ParentType.self) { r in
    Parent(child: r.resolve(ChildType.self)!)
}
container.register(ChildType.self) { _ in Child() }
    .initCompleted { r, c in
        let child = c as! Child
        child.parent = r.resolve(ParentType.self)
}

The above code is in my AppDelegate's "application:didFinishLaunchingWithOptions" function. Right after the registration code I added this little testing code:

let parent = container.resolve(ParentType.self)!
let child = container.resolve(ChildType.self)!
print(parent.getMyName())
print(child.getMyName())
print(parent.getMyChildName())
print(child.getMyParentName())

The output is this:

John
John Jr.
John Jr.
fatal error: unexpectedly found nil while unwrapping an Optional value

The error occurs on this line: return parent!.getMyName()

The weird thing is that I placed a breakpoint on that line and this is what happens:

Am I doing anything wrong with this circular dependency?

This code is in an "empty" single view iOS app, only with Swinject added as a dependency via Carthage.

XCode version 7.2.1

Swinject version 1.1 installed via Carthage

Upvotes: 3

Views: 2332

Answers (1)

Yoichi Tagaya
Yoichi Tagaya

Reputation: 4577

The problem is caused because parent property of Child is defined as a weak property.

Let's name the instances like following.

let parentA = container.resolve(ParentType.self)!
let childB = container.resolve(ChildType.self)!

Here the parent of childB is a different instance from parentA. Since parent property of childB is weak, it is set to nil after childB instance is created.

On the other hand, child property of Parent is a strong property. The instance of the child of parentA is different from childB, but the child instance is held by parentA.

If you need the direct access to a child instance, you can add var child: ChildType? { get } to ParentType protocol.

Regarding the concept of circular dependency, parentA's child's parent is parentA, which is the same instance.

If ParentType is registered as below,

container.register(ParentType.self) { r in
    Parent(child: r.resolve(ChildType.self)!)
}
.inObjectScope(.None)

parentA's child's parent and parentA are different instances.

Upvotes: 1

Related Questions