Thoughts: loss of types
guidoschmidt opened this issue · 3 comments
When playing around with the example application I kind of stumbled upon a potential issue with the implementation itself:
Having a DependencyContainer
register a service:
public extension DependencyContainer {
static var app: DependencyContainer {
return DependencyContainer { container in
container.register(as: .prototype) {
// This will register LocalStorage explicitly with it's protocol
LocalStorage() as LocalStorageProtocol
}
}
}
}
The type of the registered service/component kind of gets lost when using the inject()
method:
As a developer I may not know what type of services are registered 🤔
class ModalViewController: UIViewController {
let storage: LocalStorageProtocol = inject() // This will compile and will run
let storage: LocalStorage = inject() // This will compile but won't run
@IBAction func close(_ sender: Any) {
dismiss(animated: true)
}
}
I'm not sure if that's possible from my current understanding of the DIKit code but having something like inject(ofType: LocalStorage)
could help? Maybe the resolve()
function could be improved to use stacks of components/instances that are better typed that using a String representation? 🤔
inject(ofType: T)
does not make a lot of sense because the method currently is generic and the type gets inferred by the type of the variable. You would also need to use type(of: LocalStorage)
, thus resulting in the ugly form of:
class ModalViewController: UIViewController {
// This will compile and will run
let storage: LocalStorageProtocol = inject()
// This will compile and will run
let storage: LocalStorage = inject(type(of: LocalStorageProtocol))
// This will compile but won't run
let storage: LocalStorage = inject(type(of: LocalStorage))
@IBAction func close(_ sender: Any) {
dismiss(animated: true)
}
}
In the third example, it also would compile but won't run. To avoid those cases we would need some additional dependency graph generation and throw compile errors of the graph is not fulfilled.
What I thought about was forcing the developer to register a protocol (T == Protocol
) in order to avoid registrations of pure components. This would then throw compile errors when using the wrong protocol, i.e. `Backend() as LocalStorageProtocol).
But sadly, this is not possible with Swift as of now.
But coming back to: let storage: LocalStorage
why is it bad to use the class and not the protocol here. We want to be able to change the actual implementation (e.g. with mock objects) for testing purpose.
Maybe you come up with a different approach?
Settled for the first release even if the choice is maybe not the best the best. We can change that later on.