Circular dependencies are dependencies of instances that depend on each other. To define circular dependencies in Swinject, one of the dependencies must be injected through a property.
Assume that you have Parent and Child classes depending on each other. Parent depends on ChildProtocol through its initializer, and Child on ParentProtocol through a property. The back-reference from Child to ParentProtocol is a weak property to avoid a memory leak.
protocol ParentProtocol: AnyObject { }
protocol ChildProtocol: AnyObject { }
class Parent: ParentProtocol {
let child: ChildProtocol?
init(child: ChildProtocol?) {
self.child = child
}
}
class Child: ChildProtocol {
weak var parent: ParentProtocol?
}The circular dependencies are defined as below:
let container = Container()
container.register(ParentProtocol.self) { r in
Parent(child: r.resolve(ChildProtocol.self)!)
}
container.register(ChildProtocol.self) { _ in Child() }
.initCompleted { r, c in
let child = c as! Child
child.parent = r.resolve(ParentProtocol.self)
}Here the injection to the parent property of Child must be specified in the initCompleted callback to avoid infinite recursion.
Similarly, assume that you have the following classes depending on each other, each via a property:
protocol ParentProtocol: AnyObject { }
protocol ChildProtocol: AnyObject { }
class Parent: ParentProtocol {
var child: ChildProtocol?
}
class Child: ChildProtocol {
weak var parent: ParentProtocol?
}The circular dependencies are defined as below:
let container = Container()
container.register(ParentProtocol.self) { r in
let parent = Parent()
parent.child = r.resolve(ChildProtocol.self)!
return parent
}
container.register(ChildProtocol.self) { _ in Child() }
.initCompleted { r, c in
let child = c as! Child
child.parent = r.resolve(ParentProtocol.self)
}Here both or either of the depending properties must be specified in the initCompleted callback to avoid infinite recursion.
Not supported. This type of dependency causes infinite recursion.
When resolving circular dependencies one of the factory methods (one containing resolution of circular dependency) might be invoked twice. Only one of the resulting instances will be used in the final object graph but in some cases this can be problematic - particularly when there are side effects of factory invocation, such as
- time consuming operations
- interactions with resolved dependencies
You can avoid duplicate invocation by resolving both parts of the dependency cycle inside initCompleted closures, for example refactoring
container.register(ParentProtocol.self) { r in
let parent = Parent()
parent.child = r.resolve(ChildProtocol.self)!
return parent
}to
container.register(ParentProtocol.self) { _ in Parent() }
.initCompleted { r, p in
let parent = p as! Parent
parent.child = r.resolve(ChildProtocol.self)!
}